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

Просто разбираюсь в метапрограммировании Ruby. Примеси / модули всегда меня сбивают с толку.

  • включают: смешивается в методах указанного модуля как методы экземпляра в целевом классе
  • продлевать: смешивается в методах указанного модуля как методы класса в целевом классе

Так заключается ли основная разница только в этом или скрывается более крупный дракон? например

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!"

Также проверьте эту ссылку: juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby

Donato 20.12.2016 20:53
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
431
1
87 628
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Правильно.

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

Ruby's default implementation is to add the constants, methods, and module variables of this module to aModule if this module has not already been added to aModule or one of its ancestors.

Ответ принят как подходящий

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

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

кратко, как Конфуций.

lkahtz 23.07.2020 05:05

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

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

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

  • если вы вызовете class Klazz; include Mod; end;, теперь все экземпляры Klazz имеют доступ к методам Mod (как методы экземпляра)
  • 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 также был фантастическим ресурсом, потому что они точно документировали функциональность.

Я знаю, что это довольно старый пост, но ясность ответа не могла удержать меня от комментариев. Большое спасибо за хорошее объяснение.

MohamedSanaulla 25.12.2011 19:30

@anwar Очевидно, но теперь я могу комментировать, и мне удалось снова найти статью. Он доступен здесь: aaronlasseigne.com/2012/01/17/explaining-include-and-extend, и я все еще думаю, что схема значительно упрощает понимание

systho 09.03.2016 11:50

Большой выигрыш в этом ответе заключается в том, как extend может применять методы как методы экземпляра класса или же в зависимости от использования. Klass.extend = методы класса, objekt.extend = методы экземпляра. Я всегда (ошибочно) предполагал, что методы класса исходят от extend, а экземпляр - от include.

Frank Koehl 07.02.2018 04:53

Все остальные ответы хороши, включая совет по 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, мы добавляем связь из нашего класса с модулем, который содержит некоторые методы.

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 (объекты, классы также являются объектами) A'.

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

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

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

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

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

Другие вопросы по теме