В чем разница между include и extend в Ruby?

просто пытаюсь разобраться в метапрограммировании Руби. Mixin / modules всегда удается запутать меня.

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

так что главное различие только это или является большим драконом притаился? например,

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

6 ответов


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

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


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

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

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

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

, очень модули часто переопределить includeповедение обезьяны-латание included метод. Это очень заметно в коде legacy Rails. больше деталей от Yehuda Katz.

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

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

конечно, документация по ruby core - это всегда лучшее место для этих вещей. проект RubySpec также был фантастическим ресурсом, потому что они точно документировали функциональность.


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

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

Если вы расширения class ClassThatExtends с модулем ReusableModule, то методы и константы получает скопировал. Очевидно, что если вы не будете осторожны, вы можете потратить много памяти, динамически дублирования определений.

Если вы используете ActiveSupport::Concern, то .функция included () позволяет переписать класс including напрямую. модуль ClassMethods внутри концерна получает продлен (скопировано)в класс including.


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

Это не работает, но будет работать, если я определил его как def page_views(campaign):

class UserAction
  include Calculations

  def self.page_views(campaign)
    overall_profit =  calculate_campaign_profit(campaign)
  end
end

это работает:

class UserAction
  extend Calculations

  def self.page_views(campaign)
    overall_profit =  calculate_campaign_profit(campaign)
  end
end

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

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

class A
include MyMOd
end

a = A.new
a.some_method

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

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

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

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

а = а' => добавляться modulesto А => В => включен модуль

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