В чем разница между включением и расширением в Ruby?

Я просто разбираюсь в метапрограммировании 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!"

person Gishu    schedule 01.10.2008    source источник
comment
Также посетите эту ссылку: juixe.com/techknow /index.php/2006/06/15/mixins-in-ruby   -  person Donato    schedule 20.12.2016


Ответы (6)


То, что вы сказали, правильно. Однако это еще не все.

Если у вас есть класс Klazz и модуль Mod, включение Mod в Klazz дает экземплярам Klazz доступ к методам Mod. Или вы можете расширить Klazz с помощью Mod, предоставив классу Klazz доступ к методам Mod. Но также вы можете расширить произвольный объект с помощью o.extend Mod. В этом случае отдельный объект получает методы Mod, хотя все другие объекты с тем же классом, что и o, не получают их.

person domgblackwell    schedule 01.10.2008
comment
кратко, как Конфуций. - person lkahtz; 23.07.2020

extend - добавляет методы и константы указанного модуля в метакласс цели (т. е. в одноэлементный класс), например.

  • если вы вызовете Klazz.extend(Mod), теперь у Klazz есть методы Mod (как методы класса)
  • если вы вызываете obj.extend(Mod), теперь obj имеет методы Mod (как методы экземпляра), но ни один другой экземпляр obj.class не имеет добавленных этих методов.
  • extend - общедоступный метод

include - по умолчанию он смешивает методы указанного модуля как методы экземпляра в целевом модуле / классе. например

  • если вы вызываете class Klazz; include Mod; end;, теперь все экземпляры Klazz имеют доступ к методам мода (как методы экземпляра)
  • include - это частный метод, поскольку он предназначен для вызова из класса / модуля контейнера.

Однако модули очень часто переопределяют include поведение, исправляя метод included обезьяной. Это очень заметно в устаревшем коде Rails. дополнительные сведения от Иегуды Каца.

Дополнительные сведения о include с его поведением по умолчанию, если вы запустили следующий код

class Klazz
  include Mod
end
  • Если Mod уже включен в Klazz или в один из его предков, оператор include не действует.
  • Он также включает в себя константы мода в Klazz, если они не конфликтуют.
  • Он дает Klazz доступ к переменным модуля мода, например @@foo или @@bar
  • вызывает ArgumentError, если есть циклические включения
  • Присоединяет модуль в качестве непосредственного предка вызывающего (т.е. добавляет Mod в Klazz.ancestors, но Mod не добавляется в цепочку Klazz.superclass.superclass.superclass. Таким образом, вызов super в Klazz # foo будет проверять наличие Mod # foo перед проверка на метод foo реального суперкласса Klazz (подробности см. в RubySpec).

Конечно, лучше всего найти базовую документацию по Ruby. вещи. Проект RubySpec также был фантастическим ресурсом, поскольку они точно документировали функциональность.

person John Douthat    schedule 15.02.2011
comment
Я знаю, что это довольно старый пост, но ясность ответа не могла удержать меня от комментариев. Большое спасибо за хорошее объяснение. - person MohamedSanaulla; 25.12.2011
comment
@anwar Очевидно, но теперь я могу комментировать, и мне удалось снова найти статью. Он доступен здесь: aaronlasseigne.com/2012/01/17/ объяснение-включить-и-расширить, и я все еще думаю, что схема значительно упрощает понимание - person systho; 09.03.2016
comment
Большой выигрыш в этом ответе заключается в том, как extend может применять методы как методы экземпляра класса или, в зависимости от использования. Klass.extend = методы класса, objekt.extend = методы экземпляра. Я всегда (ошибочно) предполагал, что методы класса пришли из extend, а экземпляр из include. - person Frank Koehl; 07.02.2018

Это правильно.

За кулисами include на самом деле является псевдонимом для append_features, который (из документации):

Реализация Ruby по умолчанию заключается в добавлении констант, методов и переменных модуля этого модуля в aModule, если этот модуль еще не был добавлен в aModule или один из его предков.

person Toby Hede    schedule 01.10.2008

Когда вы include модуль в класс, методы модуля импортируются как методы экземпляра.

Однако когда вы extend модуль в класс, методы модуля импортируются как методы класса.

Например, если у нас есть модуль Module_test, определенный следующим образом:

module Module_test
  def func
    puts "M - in module"
  end
end

Теперь о модуле include. Если мы определим класс A следующим образом:

class A
  include Module_test
end

a = A.new
a.func

Результатом будет: 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 модуль, его методы становятся методами класса.

person Chintan    schedule 20.09.2019

Все остальные ответы хороши, включая совет по 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 внутри Концерна расширяется (копируется) во включающий класс.

person Ho-Sheng Hsiao    schedule 22.09.2011

Я также хотел бы объяснить механизм, как он работает. Если я не прав, поправьте.

Когда мы используем include, мы добавляем связь из нашего класса с модулем, который содержит некоторые методы.

class A
include MyMOd
end

a = A.new
a.some_method

У объектов нет методов, есть только классы и модули. Итак, когда a получает сообщение some_method, он начинает метод поиска some_method в собственном классе a, затем в A классе, а затем в связанных с A модулями класса, если они есть (в обратном порядке, последний включенный выигрывает).

Когда мы используем extend, мы добавляем связь с модулем в собственном классе объекта. Итак, если мы используем A.new.extend (MyMod), мы добавляем в наш модуль связь с собственным классом экземпляра A или классом a'. И если мы используем A.extend (MyMod), мы добавляем связь с A (объекты, классы также являются объектами) eigenclass A'.

поэтому путь поиска метода для a выглядит следующим образом: a => a '=> связанные модули с классом' class => A.

также есть метод prepend, который изменяет путь поиска:

a => a '=> добавленные модули к A => A => добавленные модули к A

Извините за мой плохой английский.

person user1136228    schedule 27.03.2016