Просто разбираюсь в метапрограммировании Ruby. Миксин / модули всегда меня смущают.
- include : миксы в указанных методах модуля как методы экземпляра в целевом классе
- extend : смешивает в указанных методах модуля как методы класса в целевом классе
Значит, главное отличие в этом или скрывается больший дракон? например
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!"
Ответы:
То, что вы сказали, правильно. Однако это еще не все.
Если у вас есть класс
Klazz
и модульMod
, в том числе иMod
вKlazz
дает примерыKlazz
доступа кMod
методам «s. Или вы можете расширитьKlazz
сMod
давая классKlazz
доступа кMod
методам «S. Но также вы можете расширить произвольный объект с помощьюo.extend Mod
. В этом случае отдельный объект получаетMod
методы, хотя все другие объекты того же класса, чтоo
и нет.источник
extend - добавляет методы и константы указанного модуля к метаклассу цели (т. е. к классу singleton), например
Klazz.extend(Mod)
, теперь у Klazz есть методы мода (как методы класса)obj.extend(Mod)
, теперь obj имеет методы Mod (как методы экземпляра), но ни один другой экземплярobj.class
не добавляет эти методы.extend
это публичный методinclude - по умолчанию он смешивает методы указанного модуля как методы экземпляра в целевом модуле / классе. например
class Klazz; include Mod; end;
, теперь все экземпляры Klazz имеют доступ к методам мода (как к методам экземпляров)include
это приватный метод, потому что он предназначен для вызова из класса / модуля контейнера.Тем не менее , модули очень часто переопределяют
include
поведение, исправляяincluded
метод. Это очень заметно в устаревшем коде Rails. больше деталей от Иегуда Каца .Дополнительные сведения о
include
поведении по умолчанию при условии, что вы запустили следующий код@@foo
или@@bar
super
Klazz # foo проверит наличие Mod # foo перед проверкой к методу foo реального суперкласса Klazz. Подробности смотрите в RubySpec.)Конечно, документация по ядру ruby всегда лучшее место для этих целей. Проект RubySpec был также фантастическим ресурсом, потому что они точно документировали функциональность.
#include
RubySpec rubydoc#included
RubySpec rubydoc#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydocисточник
extend
применять методы как методы класса или экземпляра, в зависимости от использования.Klass.extend
= методы класса,objekt.extend
= методы экземпляра. Я всегда (ошибочно) предполагал, что методы класса происходят изextend
экземпляраinclude
.Это правильно.
За кулисами include является псевдонимом для append_features , который (из документов):
источник
Когда вы
include
добавляете модуль в класс, методы модуля импортируются как методы экземпляра .Однако, когда вы
extend
добавляете модуль в класс, методы модуля импортируются как методы класса .Например, если у нас есть модуль,
Module_test
определенный следующим образом:Теперь для
include
модуля. Если мы определим классA
следующим образом:Выход будет:
M - in module
.Если заменить строку
include Module_test
сextend Module_test
и запустить код снова, мы получаем следующее сообщение об ошибке:undefined method 'func' for #<A:instance_num> (NoMethodError)
.Изменение вызова метода ,
a.func
чтобыA.func
выходной сигнал изменяется на:M - in module
.Из приведенного выше выполнения кода ясно, что когда мы
include
модуль, его методы становятся методами экземпляра, а когда мыextend
модуль, его методы становятся методами класса .источник
Все остальные ответы хороши, включая подсказку, чтобы покопаться в 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
Что касается вариантов использования:
Если включить модуль ReusableModule в классе ClassThatIncludes, получают ссылки на методы, константы, классы, подмодуль, и другие заявления.
Если вы расширяете класс ClassThatExtends модулем ReusableModule, то методы и константы копируются . Очевидно, что если вы не будете осторожны, вы можете тратить много памяти, динамически дублируя определения.
Если вы используете ActiveSupport :: Concern, функция .included () позволяет вам переписать включающий класс напрямую. Модуль ClassMethods внутри Концерна расширяется (копируется) во включающий класс.
источник
Я также хотел бы объяснить механизм, как он работает. Если я не прав, пожалуйста, поправьте.
Когда мы используем,
include
мы добавляем связь из нашего класса в модуль, который содержит несколько методов.У объектов нет методов, есть только предложения и модули. Поэтому, когда
a
получает сообщение,some_method
он начинает метод поискаsome_method
вa
собственном классе, затем вA
классе, а затем в связанном сA
модулями класса, если таковые имеются (в обратном порядке, последний включенный выигрывает).Когда мы используем,
extend
мы добавляем связь с модулем в собственном классе объекта. Поэтому, если мы используем A.new.extend (MyMod), мы добавляем связь с нашим модулем к собственному классу илиa'
классу экземпляра A. И если мы используем A.extend (MyMod), мы добавляем связь к собственному классу A (объекты, классы также являются объектами)A'
.поэтому путь поиска метода для него
a
выглядит следующим образом: a => a '=> связанные модули с a' class => A.также есть метод prepend, который изменяет путь поиска:
a => a '=> добавленные модули к A => A => включенный модуль к A
Извините за мой плохой английский.
источник