Ruby mixins: extend and include

Czytałem kilka artykułów o metodach Mixin Rubiego, extend i include i nadal nie jestem pewien zachowania. Rozumiem, że extend doda metody instancji danego modułu jako metody Singletona do modułu wykonującego Rozszerzanie, a include zasadniczo doda zawartość modułu (metody, stałe, zmienne) do tego, który wykonuje włączanie, skutecznie definiując je w odbiorniku.

Jednak, po pewnym majsterkowaniu, starając się uzyskać mam kilka pytań. Oto moja konfiguracja testowa:

module Baz
  def blorg
    puts 'blorg'
  end
end

module Bar
  include Baz
  def blah
    puts 'blah'
  end
end

module Foo
  extend Bar
end

class Bacon
  extend Bar
end

class Egg
  include Bar
end

Tak jak bym się spodziewał, moduł Bar zyskuje metody instancji zdefiniowane w Baz (#blorg) tak jakby zostały zdefiniowane same w sobie dzięki metodzie inkluzji, a klasa Bacon zyskuje metody Singletona Bacon::blah i Bacon::blorg przez rozszerzenie.

Bacon.blah  # => blah
Bacon.blorg # => blorg

I klasa Egg zyskuje metody zdefiniowane w Bar (#blah a teraz #blorg) jako metody instancji.

Egg.new.blah  # => blah
Egg.new.blorg # => blorg

Rozumiem to wszystko, więc to dobrze.

Nie rozumiem jednak odpowiedzi, które otrzymuję za pomocą metod #ancestors i #is_a?.

Bacon.ancestors  # => [Bacon, Object, Kernel, BasicObject]
Bacon.is_a? Bar  # => true

Egg.ancestors    # => [Egg, Bar, Baz, Object, Kernel, BasicObject]
Egg.is_a? Bar    # => false

Wydaje się, że rozszerzenie modułu powoduje, że metoda #is_a? zwraca true gdy zapytana o ten moduł, ale nie jest dodawana do przodków klasy i odwrotnie w odniesieniu do włączenia: przodkowie klasy zawierają moduły, które są dołączane, ale metoda #is_a? zwraca false gdy zapytana. Dlaczego tak się dzieje?

Author: DesAdams, 2013-07-09

2 answers

Różnica polega na tym, że include doda klasę included do przodków klasy including, podczas gdy extend doda klasę extended do przodków klasy singleton. UFF. Najpierw przyjrzyjmy się, co się dzieje:

Bacon.ancestors
#=> [Bacon, Object, Kernel, BasicObject]

Bacon.singleton_class.ancestors
#=> [Bar, Baz, Class, Module, Object, Kernel, BasicObject]

Bacon.new.singleton_class.ancestors
#=> [Bacon, Object, Kernel, BasicObject]

Bacon.is_a? Bar
#=> true

Bacon.new.is_a? Bar
#=> false

I dla klasy Egg

Egg.ancestors
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject]

Egg.singleton_class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]

Egg.new.singleton_class.ancestors
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject]

Egg.is_a? Bar
#=> false

Egg.new.is_a? Bar
#=> true

Więc to, co naprawdę robi foo.is_a? Klass, to sprawdzanie, czy foo.singleton_class.ancestors zawiera Klass. Inną rzeczą jest to, że wszyscy przodkowie danej klasy stają się przodkami danej klasy podczas tworzenia instancji. Tak więc będzie to oceniać na true dla wszystkich nowo utworzonych instancji dowolnej klasy:

Egg.ancestors == Egg.new.singleton_class.ancestors
Co to wszystko oznacza? extend i include robią to samo na różnych poziomach, mam nadzieję, że poniższy przykład jasno to wyjaśnia, ponieważ oba sposoby rozszerzenia klasy są zasadniczo równoważne: {]}
module A
  def foobar
    puts 'foobar'
  end
end

class B
  extend A
end

class C
  class << self
    include A
  end
end

B.singleton_class.ancestors == C.singleton_class.ancestors
#=> true

Gdzie class << self jest tylko dziwną składnią, aby dostać się do klasy singleton. Więc extend naprawdę jest skrótem include w klasie singleton.

 25
Author: Patrick Oscity,
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
2013-07-09 16:48:12
Egg.is_a? Egg # => false

Include (skutecznie) zmienia instancje klasy Egg. Chociaż nie jest to całkiem to samo, jest bardzo podobne do robienia czegoś takiego jak

class Egg < Bar
end

Kiedy extend doda metody klas, więc jest to bardzo podobne do robienia czegoś takiego jak

class Bacon
  class << self
    include Bar
  end
end

Możesz myśleć o tym jak include changes instancje klasy, gdzie as extend faktycznie zmienia klasę.

 0
Author: Olives,
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-09-07 15:24:03