Яка різниця між включенням та розширенням у Ruby?


415

Просто обводячи голову навколо метапрограмування Рубі. Mixin / модулі завжди вдається мене збентежити.

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

Тож головна відмінність саме в цьому, чи більше дракона ховається? напр

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
Донато

Відповіді:


249

Те, що ви сказали, правильно. Однак у цьому є більше, ніж це.

Якщо у вас є клас Klazzі модуль Mod, в тому числі Modв Klazzнадає екземпляри Klazzдоступу до Modметодів. Або ви можете розширити Klazzз Modдаючи клас Klazz доступу до Modметодів «S. Але також можна поширити довільний об’єкт за допомогою o.extend Mod. У цьому випадку індивідуальний об'єкт отримує Modметоди, навіть якщо всі інші об'єкти з тим самим класом, як oі ні.


324

розширити - додає методи і константи зазначеного модуля для метаклассом мети (тобто класу одноплодной) , наприклад ,

  • якщо ви телефонуєте Klazz.extend(Mod), тепер у Klazz є методи Мода (як методи класу)
  • якщо ви телефонуєте obj.extend(Mod), тепер у obj є методи Мода (як методи екземпляра), але жоден інший примірник obj.classцих методів не доданий.
  • extend є публічним методом

include - За замовчуванням він змішується в методах зазначеного модуля як методи екземпляра в цільовому модулі / класі. напр

  • якщо ви телефонуєте class Klazz; include Mod; end;, тепер усі екземпляри Klazz мають доступ до методів Мода (як методів екземпляра)
  • include це приватний метод, тому що його потрібно викликати з класу / модуля контейнера.

Однак модулі дуже часто переосмислюють include поведінку, використовуючи includedметод маніпулювання . Це дуже помітно у застарілому коді Rails. детальніше від Єгуди Кац .

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

class Klazz
  include Mod
end
  • Якщо Мод вже включений у Клац або одного з його предків, оператор include не впливає
  • Вона також включає константи Мода в Клац до тих пір, поки вони не зіткнуться
  • Це надає Клазза доступ до змінних модулів Мода, наприклад, @@fooабо@@bar
  • підвищує ArgumentError, якщо є циклічні включення
  • Приєднує модуль як безпосередній пращур абонента (тобто додає Мода до Klazz.ancestors, але Мод не додається до ланцюга Klazz.superclass.superclass.superclass. Отже, виклик superу Klazz # foo перевірить Mod # foo перед перевіркою до методу foo 'реального суперкласу Клазза. Детальніше див. у RubySpec.).

Звичайно, основна документація на рубіні завжди є найкращим місцем для цих речей. Проект RubySpec також був фантастичним ресурсом, оскільки вони точно документували функціональність.


22
Я знаю, що це досить стара публікація, але ясність відповіді не могла стримувати коментування. Дякую велике за приємне пояснення.
MohamedSanaulla

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

1
Велика виграш у цій відповіді полягає в тому, як extendможна застосовувати методи як методи класу чи екземпляра, залежно від використання. Klass.extend= методи класу, objekt.extend= методи екземпляра. Я завжди (помилково) припускав, що методи класу походять від extendі, наприклад, з include.
Френк Кель

16

Це правильно.

За лаштунками, включити насправді псевдонім для append_features , який (з документів):

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


4

Коли 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модулем, його методи стають методами класу .


3

Усі інші відповіді хороші, включаючи підказку прокопати 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 всередині концерну поширюється (копіюється) у клас, що включає.


1

Я також хотів би пояснити механізм, як він працює. Якщо я не правий, будь ласка, виправте.

Під час використання includeми додаємо зв'язок нашого класу до модуля, який містить деякі методи.

class A
include MyMOd
end

a = A.new
a.some_method

Об'єкти не мають методів, роблять лише фрази та модулі. Тож, коли aотримує повідомлення, some_methodвін починає метод пошуку some_methodв a's' eigen class, потім в Aкласі, а потім в зв'язаному з Aмодулями класу, якщо такі є (у зворотному порядку, останній включений виграш).

Під час використання extendми додаємо зв'язок до модуля в класі власності об'єкта. Отже, якщо ми використовуємо A.new.extend (MyMod), ми додаємо посилання на наш модуль до власного класу або a'класу екземпляра A. І якщо ми використовуємо A.extend (MyMod), ми додаємо зв'язок до власного класу A (об'єкта, класи також є об'єктами) A'.

тому шлях пошуку методу для a: a => a '=> пов'язані модулі до' class => A.

також існує метод додавання, який змінює шлях пошуку:

a => a '=> попередньо передбачений модуль A => A => включений модуль до A

вибачте за мою погану англійську.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.