Estou pensando na metaprogramação de Ruby. Os módulos / mixin sempre conseguem me confundir.
- incluem : combina em métodos de módulo especificados como métodos de instância na classe de destino
- estender : combina métodos de módulo especificados como métodos de classe na classe de destino
Então, a principal diferença é apenas isso ou um dragão maior está à espreita? por exemplo
module ReusableModule
def module_method
puts "Module Method: Hi there!"
end
end
class ClassThatIncludes
include ReusableModule
end
class ClassThatExtends
extend ReusableModule
end
puts "Include"
ClassThatIncludes.new.module_method # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method # "Module Method: Hi there!"
Respostas:
O que você disse está correto. No entanto, há mais do que isso.
Se você tem uma classe
Klazz
e um móduloMod
, inclusiveMod
noKlazz
fornece instâncias deKlazz
acesso aosMod
métodos. Ou você pode estenderKlazz
comMod
dando a classeKlazz
acesso aMod
métodos 's. Mas também você pode estender um objeto arbitrário como.extend Mod
. Nesse caso, o objeto individual obtémMod
os métodos de mesmo que todos os outros objetos da mesma classeo
não o sejam.fonte
estender - adiciona os métodos e constantes do módulo especificado à metaclasse do alvo (ou seja, a classe singleton), por exemplo
Klazz.extend(Mod)
, agora o Klazz tem os métodos do Mod (como métodos de classe)obj.extend(Mod)
, agora obj possui os métodos de Mod (como métodos de instância), mas nenhuma outra instância deobj.class
possui esses métodos adicionados.extend
é um método públicoinclude - Por padrão, ele combina os métodos do módulo especificado como métodos de instância no módulo / classe de destino. por exemplo
class Klazz; include Mod; end;
, agora todas as instâncias do Klazz têm acesso aos métodos do Mod (como métodos de instância)include
é um método privado, porque deve ser chamado de dentro da classe / módulo do contêiner.No entanto , os módulos muitas vezes substituem
include
o comportamento do patch do macacoincluded
. Isso é muito importante no código Rails herdado. mais detalhes de Yehuda Katz .Mais detalhes sobre
include
, com seu comportamento padrão, assumindo que você executou o seguinte código@@foo
ou@@bar
super
Klazz # foo verificará Mod # foo antes de verificar para o método foo da superclasse real de Klazz. Consulte o RubySpec para obter detalhes.)Obviamente, a documentação principal do ruby é sempre o melhor lugar para essas coisas. O projeto RubySpec também foi um recurso fantástico, porque eles documentaram a funcionalidade com precisão.
#include
RubySpec rubydoc#included
RubySpec rubydoc#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydocfonte
extend
aplicar métodos como métodos de classe ou instância, dependendo da utilização.Klass.extend
= métodos de classe,objekt.extend
= métodos de instância. Eu sempre (incorretamente) assumi que os métodos de classe vieramextend
e a instância deinclude
.Está correto.
Nos bastidores, include é na verdade um alias para append_features , que (dos documentos):
fonte
Quando você
include
insere um módulo em uma classe, os métodos do módulo são importados como métodos de instância .No entanto, quando você
extend
cria um módulo em uma classe, os métodos do módulo são importados como métodos de classe .Por exemplo, se tivermos um módulo
Module_test
definido da seguinte forma:Agora, para o
include
módulo. Se definirmos a classe daA
seguinte maneira:A saída será:
M - in module
.Se substituir a linha
include Module_test
comextend Module_test
e executar o código novamente, nós receber o seguinte erro:undefined method 'func' for #<A:instance_num> (NoMethodError)
.Alterar a chamada de método
a.func
paraA.func
, a saída muda para:M - in module
.A partir da execução do código acima, fica claro que, quando
include
um módulo, seus métodos se tornam métodos de instância e, quandoextend
um módulo, seus métodos se tornam métodos de classe .fonte
Todas as outras respostas são boas, incluindo a dica para pesquisar no RubySpecs:
https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb
https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb
Quanto aos casos de uso:
Se você incluir o módulo ReusableModule na classe ClassThatIncludes, os métodos, constantes, classes, submódulos e outras declarações serão referenciados.
Se você estender a classe ClassThatExtends com o módulo ReusableModule, os métodos e constantes serão copiados . Obviamente, se você não for cuidadoso, poderá desperdiçar muita memória duplicando dinamicamente as definições.
Se você usa o ActiveSupport :: Concern, a funcionalidade .included () permite reescrever a classe incluindo diretamente. O módulo ClassMethods dentro de uma preocupação é estendido (copiado) para a classe inclusive.
fonte
Eu também gostaria de explicar o mecanismo como ele funciona. Se eu não estiver certo, corrija.
Quando usamos
include
, estamos adicionando uma ligação da nossa classe a um módulo que contém alguns métodos.Objetos não têm métodos, apenas classes e módulos. Portanto, quando
a
recebe uma mensagem,some_method
ele começa o método de buscasome_method
naa
classe eigen, depois naA
classe e depois nosA
módulos da classe, se houver algum (em ordem inversa, as últimas vitórias incluídas).Quando usamos
extend
, estamos adicionando ligação a um módulo na classe eigen do objeto. Então, se nós usamos A.new.extend (MyMod) estamos adicionando ligação com o nosso módulo de classe instância eigen de A oua'
classe. E se usarmos A.extend (MyMod), estamos adicionando ligação à classe própria de A (do objeto, as classes também são objetos)A'
.portanto, o caminho de pesquisa do método
a
é o seguinte: a => a '=> módulos vinculados a uma' classe => A.também existe um método de prefpend que altera o caminho da pesquisa:
a => a '=> módulos anexados a A => A => módulo incluído em A
desculpe pelo meu inglês ruim.
fonte