Ruby'de include ve expand arasındaki fark nedir?


415

Sadece kafamı Ruby meta programlamasının etrafında gezdirmek. Mixin / modüller her zaman beni şaşırtmayı başarır.

  • include : hedef sınıfta örnek yöntemler olarak belirtilen modül yöntemlerindeki karışımlar
  • expand : hedef sınıfta sınıf yöntemleri olarak belirtilen modül yöntemlerini karıştırır

Büyük fark sadece bu mu yoksa daha büyük bir ejderha gizleniyor mu? Örneğin

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

Yanıtlar:


249

Söylediklerin doğru. Ancak bundan daha fazlası var.

Eğer bir sınıf varsa Klazzve modül Moddahil Modiçinde Klazzörneklerini verir Klazzerişim Mod'ın yöntemlerle. Yoksa uzatabilirsiniz Klazzile Modverilmesi sınıf Klazz erişimi Modbireyin yöntemlerle. Ama ayrıca keyfi bir nesneyi genişletebilirsiniz o.extend Mod. Bu durumda, Modaynı sınıftaki diğer tüm nesneler olmasa da , tek tek nesnenin yöntemleri alınır o.


324

genişletme - belirtilen modülün yöntemlerini ve sabitlerini hedefin meta sınıfına (örn. singleton sınıfı) ekler örn.

  • Eğer ararsanız Klazz.extend(Mod), şimdi Klazz Mod'un yöntemlerine sahiptir (sınıf yöntemleri olarak)
  • çağırırsanız obj.extend(Mod), şimdi obj Mod'un yöntemlerine (örnek yöntemleri olarak) sahiptir, ancak başka hiçbir örneği obj.classbu yöntemlere eklenmez.
  • extend herkese açık bir yöntemdir

include - Varsayılan olarak, belirtilen modülün yöntemlerini hedef modül / sınıftaki örnek yöntemler olarak karıştırır. Örneğin

  • çağırırsanız class Klazz; include Mod; end;, şimdi tüm Klazz örnekleri Mod'un yöntemlerine erişebilir (örnek yöntemler olarak)
  • include kapsayıcı sınıf / modül içinden çağrılması amaçlandığından özel bir yöntemdir.

Bununla birlikte , modüller yöntemin maymunu yatırarak davranışlarını çoğunlukla geçersiz kılar . Bu, eski Rails kodunda çok belirgindir. daha fazla ayrıntı Yehuda Katz .includeincluded

includeAşağıdaki kodu çalıştırdığınız varsayılarak varsayılan davranışı hakkında daha fazla ayrıntı

class Klazz
  include Mod
end
  • Mod zaten Klazz'a veya atalarından birine dahil edilmişse, include deyiminin hiçbir etkisi yoktur
  • Ayrıca, çatışmadıkları sürece Mod'un Klazz'daki sabitlerini de içerir.
  • Bu örneğin Mod en modül değişkenleri için Klazz erişim sağlar @@fooveya@@bar
  • döngüsel içerikler varsa ArgumentError öğesini yükseltir
  • Ateşelikler arayanın acil atası olarak modül (yani O Klazz.ancestors Mod ekler, ancak Mod çağırarak, Klazz.superclass.superclass.superclass. So zinciri eklenmez superdenetimi öncesinde Mod # foo kontrol edecektir Klazz # foo Klazz'ın gerçek süper sınıfının foo yöntemine gidin. Ayrıntılar için RubySpec'e bakın.).

Tabii ki, yakut çekirdek belgeleri bu şeyler için her zaman en iyi yerdir. RubySpec projesi de harika bir kaynaktı çünkü işlevselliği tam olarak belgelediler.


22
Bunun oldukça eski bir yazı olduğunu biliyorum, ama cevabın netliği beni yorumdan alıkoyamadı. Güzel bir açıklama için çok teşekkürler.
MohamedSanaulla

2
@anwar Açıkçası, ama şimdi yorum yapabilirim ve makaleyi tekrar bulmayı başardım. Burada mevcuttur: aaronlasseigne.com/2012/01/17/explaining-include-and-extend ve şemanın hala anlayışı çok daha kolay hale getirdiğini düşünüyorum
systho

1
Bu yanıttaki en büyük kazanç , kullanıma bağlı olarak yöntemleri extendsınıf veya örnek yöntemler olarak nasıl uygulayabileceğidir . Klass.extend= sınıf yöntemleri, objekt.extend= örnek yöntemleri. Her zaman (yanlış) sınıf yöntemlerinin geldiğini extendve örneğinden geldiğini varsaydım include.
Frank Koehl

16

Bu doğru.

Kamera arkası dahil için bir takma ad aslında append_features , (dokümanlardan):

Ruby'nin varsayılan uygulaması, bu modül aModule veya atalarından birine eklenmemişse bu modülün sabitlerini, yöntemlerini ve modül değişkenlerini aModule'a eklemektir.


5

Bir includesınıfı sınıfa dönüştürdüğünüzde, modül yöntemleri örnek yöntemleri olarak içe aktarılır .

Ancak, extendbir modülü sınıfa dönüştürdüğünüzde, modül yöntemleri sınıf yöntemleri olarak alınır .

Örneğin, Module_testaşağıdaki gibi tanımlanmış bir modülümüz varsa :

module Module_test
  def func
    puts "M - in module"
  end
end

Şimdi, includemodül için. Sınıfı Aaşağıdaki gibi tanımlarsak:

class A
  include Module_test
end

a = A.new
a.func

Çıkış olacaktır: M - in module.

Çizgiyi değiştirin include Module_testile extend Module_testve kodun yeniden çalıştırın, aşağıdaki hatayı alırsınız: undefined method 'func' for #<A:instance_num> (NoMethodError).

Yöntem çağrısı değiştirilmesi a.funciçin A.func, çıkış dönüşür: M - in module.

Yukarıdaki kod yürütülmesinden, includebir modül olduğunda, yöntemlerinin örnek yöntemler ve biz extendbir modül olduğunda, yöntemlerinin sınıf yöntemleri haline geldiği açıktır .


3

RubySpecs ile kazma ipucu da dahil olmak üzere diğer tüm cevaplar iyidir:

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

Kullanım durumlarına gelince:

Eğer varsa şunlardır sınıf ClassThatIncludes modül ReusableModule, yöntemler, sabitler, sınıflar, alt modüller ve diğer beyanlar başvurulan alır.

Eğer varsa uzatmak modül ReusableModule ile sınıf ClassThatExtends ardından yöntemler ve sabitler alır kopyalanamaz . Açıkçası, dikkatli değilseniz, tanımları dinamik olarak çoğaltarak çok fazla bellek harcayabilirsiniz.

ActiveSupport :: Concern kullanıyorsanız, .included () işlevi, dahil sınıfını doğrudan yeniden yazmanıza olanak tanır. Bir Concern içindeki ClassMethods modülü , dahil edilen sınıfa genişletilir (kopyalanır).


1

Ayrıca mekanizmayı çalışırken açıklamak istiyorum. Eğer doğru değilsem lütfen düzeltin.

Kullandığımızda include, sınıfımızdan bazı yöntemler içeren bir modüle bir bağlantı ekliyoruz.

class A
include MyMOd
end

a = A.new
a.some_method

Nesnelerin yöntemleri yoktur, sadece sınıflar ve modüller vardır. Yani amesage aldığı some_methodarama yöntemi başlamak some_methodiçinde adaha sonra, 's öz sınıfına Abağlı sonra sınıf ve Aeğer var ise sınıf modüllerinde (ters düzen son kazanır dahil).

Kullandığımızda extend, nesnenin öz sınıfındaki bir modüle bağlantı ekliyoruz. Yani A.new.extend (MyMod) kullanırsak modülümüze A örneği eigen sınıfına veya a'sınıfına bağlantı ekliyoruz . Ve eğer A.extend (MyMod) kullanırsak, A (nesnenin sınıfları da nesnelerdir) eigenclassına bağlantı ekliyoruz A'.

bu nedenle yöntem arama yolu aaşağıdaki gibidir: a => a '=>' class => A ile bağlantılı modüller.

Ayrıca arama yolunu değiştiren bir başa ekleme yöntemi vardır:

a => a '=> A => A => modülünü A'ya ekledi

kötü ingilizcem için özür dilerim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.