Qual é a diferença entre incluir e estender em Ruby?
Pergunta
Apenas começando minha cabeça em torno de Ruby metaprogramming. Os mixin / módulos sempre conseguem me confundir.
-
incluem : misturas em métodos módulo especificado como métodosinstância na classe alvo -
estender : misturas em métodos módulo especificado como métodosclasse na classe alvo
Então é a principal diferença apenas isso ou é um dragão maior à 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!"
Solução
O que você disse é correto. No entanto, há mais do que isso.
Se você tem um Klazz
classe e Mod
módulo, incluindo Mod
em Klazz
dá exemplos de acesso Klazz
aos métodos de Mod
. Ou você pode estender Klazz
com Mod
dando a classe acesso Klazz
aos métodos de Mod
. Mas também é possível estender um objeto arbitrário com o.extend Mod
. Neste caso, o objeto individual fica métodos de Mod
mesmo que todos os outros objetos com a mesma classe que o
não.
Outras dicas
- se você chamar
Klazz.extend(Mod)
, agora Klazz tem métodos de modificação (como métodos de classe) - se você chamar
obj.extend(Mod)
, agora obj tem métodos de modificação (como métodos de instância), mas nenhuma outra instância deobj.class
tem esses métodos adicionais. -
extend
é um método público
incluir - Por padrão, ele mistura em métodos do módulo especificado como métodos de instância do módulo de destino / aula. por exemplo.
- se você chamar
class Klazz; include Mod; end;
, agora todas as instâncias do Klazz têm acesso a métodos de modificação (como métodos de instância) -
include
é um método particular, porque está destinado a ser chamado de dentro da classe container / módulo.
No entanto , módulos muitas vezes substituir o comportamento de include
by-remendar macaco o método included
. Isto é muito proeminente no código Rails legados. mais detalhes Yehuda Katz .
Mais detalhes sobre include
, com seu comportamento padrão, assumindo que você execute o seguinte código
class Klazz
include Mod
end
- Se Mod já está incluído no Klazz, ou um de seus antepassados, a incluir declaração não tem efeito
- Ele também inclui constantes da modificação em Klazz, contanto que eles não colidir
- Ela dá acesso Klazz a variáveis ??módulo de modificação, por exemplo,
@@foo
ou@@bar
- levanta ArgumentError se houver cíclica inclui
- Anexa o módulo como ancestral imediato do chamador (ou seja Acrescenta Mod para Klazz.ancestors, mas Mod não é adicionado à cadeia de Klazz.superclass.superclass.superclass. Então, chamando
super
em Klazz # foo irá verificar se há Mod #foo antes de verificar para o método foo verdadeiro de superclasse de Klazz. Veja a RubySpec para mais detalhes.).
Claro, a documentação núcleo rubi é sempre o melhor lugar para ir para essas coisas. O projeto RubySpec também foi um recurso fantástico, porque eles documentaram a funcionalidade precisão.
- #include href="https://github.com/ruby/rubyspec/blob/master/core/module/include_spec.rb" rel="noreferrer"> RubySpec rubydoc
- #included href="https://github.com/ruby/rubyspec/blob/master/core/module/included_spec.rb" rel="noreferrer"> RubySpec rubydoc
- #extend href="https://github.com/ruby/rubyspec/blob/master/core/kernel/extend_spec.rb" rel="noreferrer"> RubySpec rubydoc
- #extended href="https://github.com/ruby/rubyspec/blob/master/core/module/extended_spec.rb" rel="noreferrer"> RubySpec rubydoc
- #extend_object href="https://github.com/ruby/rubyspec/blob/master/core/module/extend_object_spec.rb" rel="noreferrer"> RubySpec rubydoc
- #append_features href="https://github.com/ruby/rubyspec/blob/master/core/module/append_features_spec.rb" rel="noreferrer"> RubySpec rubydoc
Isso é correto.
Nos bastidores, incluem é realmente um alias para append_features , que (a partir dos docs):
implementação padrão do Ruby é adicione a constantes, métodos e módulo variáveis ??deste módulo para aModule se este módulo já não foi adicionado para aModule ou um de seus antepassados.
Todas as outras respostas são bons, incluindo a ponta que cavar através RubySpecs:
https://github.com/rubyspec/rubyspec/ blob / master / core / módulo / include_spec.rb
https://github.com/rubyspec/rubyspec/ blob / master / core / módulo / extend_object_spec.rb
Como para casos de uso:
Se você incluir módulo ReusableModule em ClassThatIncludes classe, os métodos, constantes, aulas, submódulos e outras declarações fica referenciado.
Se você estender ClassThatExtends classe com o módulo ReusableModule, em seguida, os métodos e constantes recebe Copiado . Obviamente, se você não for cuidadoso, você pode perder uma grande quantidade de memória por definições duplicação dinamicamente.
Se você usar ActiveSupport :: Concern, a funcionalidade .included () permite reescrever a classe, incluindo diretamente. módulo ClassMethods dentro de uma preocupação fica estendido (copiados) para a classe incluindo.
Eu também gostaria de explicar o mecanismo de como ele funciona. Se eu não estou certo agradar correta.
Quando usamos include
estamos adicionando uma ligação da nossa classe para um módulo que contém alguns métodos.
class A
include MyMOd
end
a = A.new
a.some_method
Objetos não têm métodos, apenas clases e módulos de fazer.
Então, quando a
recebe some_method
mesage que começar procurar método some_method
na classe de a
Eigen, em seguida, na classe A
e, em seguida, ligado a módulos de classe A
se houver algum (em ordem inversa, ú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 ou a'
classe instância Eigen da A.
E se usarmos A.extend (MyMod) estamos adicionando ligação com A (objeto de, classes também são objetos) eigenclass A'
.
Assim o método de pesquisa caminho para a
é a seguinte:
a => a '=> módulos ligados a um' class => A.
Também existe um método prepend que muda caminho de pesquisa:
a => a'=> prepended modulesto A => A => módulo incluído a A
desculpe pelo meu mau Inglês.