Jaka jest różnica między include i extend w Ruby?

Zaczynam myśleć o Metaprogramowaniu Ruby. Mixin / modules zawsze udaje mi się zmylić.

  • include : mixuje w określonych metodach modułu jako metody instancji w klasie docelowej
  • extend : miesza się w określonych metodach modułu jako metody klasy w klasie docelowej

więc czy główna różnica to tylko to, czy czy czyhający większy smok? np.

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!"
Author: nbro, 2008-10-01

6 answers

To, co powiedziałeś, jest poprawne. Jednak jest w tym coś więcej.

Jeśli masz klasę Klazz i moduł Mod, w tym Mod w Klazz daje instancje Klazz dostęp do metod Mod. Możesz też rozszerzyć Klazz z Mod dając klasę Klazz dostęp do metod Mod. Ale możesz również rozszerzyć dowolny obiekt za pomocą o.extend Mod. W tym przypadku pojedynczy obiekt otrzymuje metody Mod, mimo że wszystkie inne obiekty z tą samą klasą co o nie.

 221
Author: domgblackwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-07 02:32:47

Extend - dodaje metody i stałe podanego modułu do metaklasy celu (tj. klasy singleton) np.

  • Jeśli wywołasz Klazz.extend(Mod), Teraz Klazz ma metody Mod (jako metody klasowe)
  • Jeśli wywołasz obj.extend(Mod), Teraz obj ma metody Mod( jako metody instancji), ale żadna inna instancja obj.class nie ma tych metod dodanych.
  • extend jest metodą publiczną

Include - domyślnie miesza się w podanym module metody jako metody instancji w docelowym module / klasie. np.

  • Jeśli wywołasz class Klazz; include Mod; end;, Teraz wszystkie instancje Klazz mają dostęp do metod Moda (jako metody instancji)
  • {[6] } jest metodą prywatną, ponieważ jest przeznaczona do wywołania z poziomu klasy/modułu kontenera.

Jednak, Moduły bardzo często nadpisują include's zachowanie przez małpę-łatanie metodą included. Jest to bardzo widoczne w kodzie legacy Rails. więcej szczegółów od Yehuda Katz .

Dalsze szczegóły na temat include, z domyślnym zachowaniem, zakładając, że uruchomiłeś następujący kod

class Klazz
  include Mod
end
  • Jeśli Mod jest już zawarty w Klazz, lub jeden z jego przodków, oświadczenie include nie ma wpływu
  • zawiera również stałe Mod w Klazz, o ile nie kolidują ze sobą
  • daje dostęp do zmiennych modułu Mod, np. @@foo lub @@bar
  • podnosi ArgumentError jeśli istnieją cykliczne zawiera
  • dołącza moduł jako najbliższy przodek wywołującego (tzn. dodaje Mod do Klazz.przodków, ale Mod nie jest dodawany do łańcucha Klazz.superklasa.superklasa.superklasa. Tak więc wywołanie super W Klazz#foo sprawdzi Mod#foo przed sprawdzeniem do prawdziwej metody Klazz 's superclass' s foo. Szczegóły w rubyspec.).

Oczywiście, dokumentacja ruby core jest zawsze najlepszym miejscem na takie rzeczy. projekt RubySpec był również fantastyczny zasób, ponieważ dokładnie udokumentowali funkcjonalność.

 290
Author: John Douthat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-22 21:53:23

Zgadza się.

Za kulisami, include jest w rzeczywistości aliasem dla append_features , który (z dokumentów):

Domyślną implementacją Ruby jest dodaj stałe, metody i moduł zmienne tego modułu do aModule jeżeli ten moduł nie został jeszcze dodany do aModule lub jednego z jej przodków.

 13
Author: Toby Hede,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 08:08:30

Wszystkie inne odpowiedzi są dobre, w tym wskazówka do kopania przez 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

Co do przypadków użycia:

Jeśli dołączysz Moduł ReusableModule do klasy ClassThatIncludes, metody, stałe, klasy, podmodule i inne deklaracje zostaną odwołane.

Jeśli rozszerzysz klasę ClassThatExtends z modułem ReusableModule, wtedy metody i stałe zostaną skopiowane . Oczywiście, jeśli nie jesteś ostrożny, możesz zmarnować dużo pamięci przez dynamiczne powielanie definicji.

Jeśli używasz Activesupport:: Concern, the .funkcja included () umożliwia bezpośrednie przepisanie klasy including. moduł ClassMethods wewnątrz problemu otrzymuje rozszerzone (skopiowane) do klasy including.

 4
Author: Ho-Sheng Hsiao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-22 21:02:06

Nauczyłem się go wcześniej, ale doceniam to, gdy go używam. Oto różnica:

To nie działa, ale działałoby, gdybym zdefiniował to jako def page_views(campaign):

class UserAction
  include Calculations

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

To działa:

class UserAction
  extend Calculations

  def self.page_views(campaign)
    overall_profit =  calculate_campaign_profit(campaign)
  end
end
 1
Author: Caner Çakmak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 12:21:13

Chciałbym również wyjaśnić mechanizm, jak to działa. Jeśli nie mam racji, proszę poprawić.

Kiedy używamy include dodajemy link z naszej klasy do modułu, który zawiera pewne metody.

class A
include MyMOd
end

a = A.new
a.some_method

Obiekty nie mają metod, tylko klasy i moduły. Tak więc, gdy a otrzymuje mesage some_method, rozpoczyna wyszukiwanie metody some_method w klasie eigen a, Następnie w klasie A, a następnie w modułach klasy linked to A, jeśli są jakieś (w odwrotnej kolejności, ostatnio włączone zwycięstwa).

Kiedy używamy extend dodajemy linkowanie do modułu w klasie eigen obiektu. Więc jeśli użyjemy A. new.extend (MyMod) dodajemy link do naszego modułu do instancji klasy eigen lub klasy a'. I jeśli użyjemy A. extend(MyMod) dodajemy linkowanie do a (object ' s, klasy są również obiektami) eigenclass A'.

Więc ścieżka wyszukiwania metody dla a jest następująca: a= > A' = > połączone moduły do A ' class = > A.

Istnieje również metoda prepend, która zmienia lookup ścieżka:

A => a' = > dołączony moduł do A => a = > dołączony moduł do a

Przepraszam za zły angielski.
 1
Author: user1136228,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-27 12:11:02