Apa perbedaan antara menyertakan dan memperluas di Ruby?

415

Hanya memusatkan kepalaku pada pemrograman Ruby. Modul / mixin selalu berhasil membingungkan saya.

  • termasuk : campuran dalam metode modul yang ditentukan sebagai metode instance di kelas target
  • extended : mencampur metode modul yang ditentukan sebagai metode kelas di kelas target

Jadi apakah perbedaan utama hanya ini atau apakah naga yang lebih besar mengintai? misalnya

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

Jawaban:

249

Apa yang Anda katakan itu benar. Namun ada yang lebih dari itu.

Jika Anda memiliki kelas Klazzdan modul Mod, termasuk Moddalam Klazzmemberikan contoh Klazzakses ke Modmetode. Atau Anda dapat memperluas Klazzdengan Modmemberikan akses kelas Klazz ke Modmetode. Tetapi Anda juga dapat memperluas objek sewenang-wenang o.extend Mod. Dalam hal ini objek individu mendapatkan Modmetode meskipun semua objek lain dengan kelas yang sama otidak.

domgblackwell
sumber
324

extended - menambahkan metode dan konstanta modul yang ditentukan ke metaclass target (mis. kelas singleton) misalnya

  • jika Anda menelepon Klazz.extend(Mod), sekarang Klazz memiliki metode Mod (sebagai metode kelas)
  • jika Anda memanggil obj.extend(Mod), sekarang obj memiliki metode Mod (sebagai metode instance), tetapi tidak ada instance lain dari obj.classmetode tersebut yang ditambahkan.
  • extend adalah metode publik

termasuk - Secara default, ia bercampur dalam metode modul yang ditentukan sebagai metode instance dalam modul / kelas target. misalnya

  • jika Anda menelepon class Klazz; include Mod; end;, sekarang semua instance Klazz memiliki akses ke metode Mod (sebagai metode instance)
  • include adalah metode pribadi, karena ini dimaksudkan untuk dipanggil dari dalam kelas / modul wadah.

Namun , modul sangat sering mengesampingkan include perilaku dengan cara menambal monyet included. Ini sangat menonjol dalam kode Rails lama. lebih detail dari Yehuda Katz .

Rincian lebih lanjut tentang include, dengan perilaku default-nya, dengan asumsi Anda telah menjalankan kode berikut

class Klazz
  include Mod
end
  • Jika Mod sudah termasuk dalam Klazz, atau salah satu leluhurnya, pernyataan sertakan tidak berpengaruh
  • Ini juga termasuk konstanta Mod di Klazz, selama mereka tidak berbenturan
  • Ini memberi Klazz akses ke variabel modul Mod, misalnya @@fooatau@@bar
  • memunculkan ArgumentError jika ada cyclic termasuk
  • Melampirkan modul sebagai nenek moyang langsung penelepon (yaitu Ia menambah Mod ke Klazz.ancestors, tetapi Mod tidak ditambahkan ke rantai Klazz.superclass.superclass.superclass. Jadi, memanggil superKlazz # foo akan memeriksa Mod # foo sebelum memeriksa untuk metode foo superclass nyata Klazz. Lihat RubySpec untuk detailnya.).

Tentu saja, dokumentasi inti ruby selalu menjadi tempat terbaik untuk hal-hal ini. Proyek RubySpec juga merupakan sumber yang fantastis, karena mereka mendokumentasikan fungsionalitasnya dengan tepat.

John Douthat
sumber
22
Saya tahu ini posting yang cukup lama, tetapi kejelasan jawabannya tidak dapat menahan saya untuk tidak berkomentar. Terima kasih banyak untuk penjelasannya.
MohamedSanaulla
2
@war Jelas, tapi sekarang saya bisa berkomentar dan saya berhasil menemukan artikel itu lagi. Ini tersedia di sini: aaronlasseigne.com/2012/01/17/explaining-include-and-extend dan saya masih berpikir skema membuat pemahaman lebih mudah
systho
1
Kemenangan besar dalam respons ini adalah bagaimana extendbisa menerapkan metode sebagai metode kelas atau contoh, tergantung pada pemanfaatan. Klass.extend= metode kelas, objekt.extend= metode instan. Saya selalu (salah) mengasumsikan metode kelas berasal extend, dan contoh dari include.
Frank Koehl
16

Itu benar.

Di belakang layar, termasuk sebenarnya adalah alias untuk append_features , yang (dari dokumen):

Implementasi standar Ruby adalah menambahkan konstanta, metode, dan variabel modul dari modul ini ke aModule jika modul ini belum ditambahkan ke aModule atau salah satu leluhurnya.

Toby Hede
sumber
5

Ketika Anda includemodul ke dalam kelas, metode modul diimpor sebagai metode contoh .

Namun, ketika Anda extendmodul ke dalam kelas, metode modul diimpor sebagai metode kelas .

Sebagai contoh, jika kita memiliki modul yang Module_testdidefinisikan sebagai berikut:

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

Sekarang, untuk includemodul. Jika kita mendefinisikan kelas Asebagai berikut:

class A
  include Module_test
end

a = A.new
a.func

Output akan: M - in module.

Jika kita mengganti baris include Module_testdengan extend Module_testdan menjalankan kode lagi, kami menerima kesalahan berikut: undefined method 'func' for #<A:instance_num> (NoMethodError).

Mengubah metode panggilan a.funcuntuk A.func, output berubah menjadi: M - in module.

Dari eksekusi kode di atas, jelas bahwa ketika kita includesebuah modul, metodenya menjadi metode instan dan ketika kita extendsebuah modul, metodenya menjadi metode kelas .

Chintan
sumber
3

Semua jawaban lain baik, termasuk tip untuk menggali melalui 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

Adapun kasus penggunaan:

Jika Anda menyertakan modul ReusableModule di kelas ClassThatIncludes, metode, konstanta, kelas, submodula, dan deklarasi lainnya akan direferensikan.

Jika Anda memperluas class ClassThatExtends dengan modul ReusableModule, maka metode dan konstanta akan disalin . Jelas, jika Anda tidak hati-hati, Anda dapat membuang banyak memori dengan menduplikasi definisi secara dinamis.

Jika Anda menggunakan ActiveSupport :: Concern, fungsionalitas .included () memungkinkan Anda menulis ulang kelas yang disertakan secara langsung. module ClassMethods di dalam Concern akan diperluas (disalin) ke dalam kelas yang disertakan .

Ho-Sheng Hsiao
sumber
1

Saya juga ingin menjelaskan mekanisme kerjanya. Jika saya salah, mohon koreksi.

Ketika kami menggunakan, includekami menambahkan tautan dari kelas kami ke modul yang berisi beberapa metode.

class A
include MyMOd
end

a = A.new
a.some_method

Objek tidak memiliki metode, hanya modul dan klase yang melakukannya. Jadi ketika amenerima pesan some_methoditu mulai metode pencarian some_methoddi akelas eigen, kemudian di Akelas dan kemudian di tautkan keA modul kelas jika ada beberapa (dalam urutan terbalik, kemenangan terakhir termasuk).

Ketika kami menggunakan extendkami menambahkan tautan ke modul di kelas eigen objek. Jadi jika kita menggunakan A.new.extend (MyMod) kita menambahkan tautan ke modul kita ke kelas contoh eigen atau a'kelas A. Dan jika kita menggunakan A.extend (MyMod) kita menambahkan linkage ke A (object, class juga object) eigenclass A'.

jadi jalur pencarian metode aadalah sebagai berikut: a => a '=> modul tertaut ke a' class => A.

juga ada metode prepend yang mengubah jalur pencarian:

a => a '=> modul bergantung pada A => A => termasuk modul ke A

maaf untuk bahasa inggris saya yang buruk.

pengguna1136228
sumber