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

StackOverflow https://stackoverflow.com/questions/156362

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Решение

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

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

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

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

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

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

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

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

Более подробная информация о include, с поведением по умолчанию, при условии, что вы запустили следующий код

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

Конечно, документация ядра Ruby всегда лучшее место для этих вещей. Проект РубиСпец также был фантастическим ресурсом, потому что они точно документировали функциональность.

Правильно.

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

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

Все остальные ответы хороши, включая совет покопаться в 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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top