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?
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.
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ę.
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