Saya baru saja membaca tentang topik ini di The Well-Grounded Rubyist (buku bagus, omong-omong). Penulis melakukan pekerjaan menjelaskan dengan lebih baik daripada saya, jadi saya akan mengutipnya:
Tidak ada aturan atau formula tunggal yang selalu menghasilkan desain yang tepat. Tetapi penting untuk mengingat beberapa pertimbangan saat Anda membuat keputusan kelas-versus-modul:
Modul tidak memiliki instance. Oleh karena itu entitas atau hal-hal umumnya dimodelkan terbaik di kelas, dan karakteristik atau properti entitas atau hal-hal terbaik dikemas dalam modul. Sejalan dengan itu, sebagaimana dicatat dalam bagian 4.1.1, nama kelas cenderung menjadi kata benda, sedangkan nama modul sering merupakan kata sifat (Stack versus Stacklike).
Kelas hanya dapat memiliki satu superclass, tetapi ia dapat mencampur modul sebanyak yang diinginkan. Jika Anda menggunakan warisan, berikan prioritas untuk menciptakan hubungan superclass / subclass yang masuk akal. Jangan menggunakan hubungan satu-satunya kelas superclass untuk memberkahi kelas dengan apa yang bisa berubah menjadi hanya satu dari beberapa set karakteristik.
Ringkas aturan-aturan ini dalam satu contoh, inilah yang tidak boleh Anda lakukan:
module Vehicle
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...
Sebaliknya, Anda harus melakukan ini:
module SelfPropelling
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...
Versi kedua memodelkan entitas dan properti jauh lebih rapi. Truk turun dari Kendaraan (yang masuk akal), sedangkan SelfPropelling adalah karakteristik kendaraan (setidaknya, semua yang kita pedulikan dalam model dunia ini) —karakteristik yang diteruskan ke truk berdasarkan truk sebagai keturunan, atau bentuk khusus, Kendaraan.
Truck
IS AVehicle
- tidak adaTruck
yang tidak akan menjadiVehicle
. Namun saya akan memanggil modul mungkinSelfPropelable
(:?) HmmSelfPropeled
kedengarannya benar, tetapi hampir sama: D. Lagi pula saya tidak akan memasukkannya keVehicle
dalamTruck
- karena ada Kendaraan yang TIDAKSelfPropeled
. Juga indikasi yang baik untuk bertanya - apakah ada Hal-hal lain, BUKAN Kendaraan yang ADALAHSelfPropeled
? - Yah mungkin, tapi aku akan lebih sulit ditemukan. JadiVehicle
bisa mewarisi dari SelfPropelling kelas (sebagai kelas itu tidak akan cocokSelfPropeled
- karena lebih dari peran)Saya pikir mixin adalah ide bagus, tapi ada masalah lain di sini yang tidak seorang pun sebutkan: tabrakan namespace. Mempertimbangkan:
Yang mana yang menang? Di Ruby, ternyata yang terakhir
module B
,, karena Anda memasukkannya setelahmodule A
. Sekarang, sangat mudah untuk menghindari masalah ini: pastikan semuamodule A
danmodule B
's konstanta dan metode dalam ruang nama tidak mungkin. Masalahnya adalah bahwa kompiler tidak memperingatkan Anda sama sekali ketika tabrakan terjadi.Saya berpendapat bahwa perilaku ini tidak skala ke tim besar programmer - Anda tidak boleh berasumsi bahwa orang yang menerapkan
class C
tahu tentang setiap nama dalam ruang lingkup. Ruby bahkan akan membiarkan Anda mengganti konstanta atau metode dari tipe yang berbeda . Saya tidak yakin yang bisa pernah dianggap perilaku yang benar.sumber
C#sayhi
menghasilkanB::HELLO
bukan karena Ruby mencampur konstanta, tetapi karena ruby menyelesaikan konstanta dari lebih dekat ke jauh - jadiHELLO
dirujuk diB
akan selalu diselesaikanB::HELLO
. Ini berlaku bahkan jika kelas C mendefinisikannya sendiriC::HELLO
juga.Pilihan saya: Modul adalah untuk berbagi perilaku, sedangkan kelas untuk memodelkan hubungan antar objek. Secara teknis Anda bisa menjadikan semuanya sebagai instance dari objek dan menggabungkan modul apa pun yang Anda inginkan untuk mendapatkan serangkaian perilaku yang diinginkan, tetapi itu akan menjadi desain yang buruk, serampangan, dan agak tidak terbaca.
sumber
Jawaban atas pertanyaan Anda sebagian besar kontekstual. Menyaring pengamatan pubb, pilihan terutama didorong oleh domain yang dipertimbangkan.
Dan ya, ActiveRecord seharusnya dimasukkan daripada diperluas oleh subkelas. ORM lain - datamapper - justru mencapai itu!
sumber
Saya sangat menyukai jawaban Andy Gaskell - hanya ingin menambahkan bahwa ya, ActiveRecord tidak boleh menggunakan pewarisan, melainkan menyertakan modul untuk menambahkan perilaku (kebanyakan kegigihan) ke model / kelas. ActiveRecord hanya menggunakan paradigma yang salah.
Untuk alasan yang sama, saya sangat menyukai MongoId daripada MongoMapper, karena membuat pengembang berkesempatan untuk menggunakan warisan sebagai cara memodelkan sesuatu yang bermakna dalam domain masalah.
Sangat menyedihkan bahwa tidak ada seorang pun di komunitas Rails yang menggunakan "Ruby inheritance" seperti yang seharusnya digunakan - untuk mendefinisikan hierarki kelas, bukan hanya untuk menambahkan perilaku.
sumber
Cara terbaik saya memahami mixin adalah sebagai kelas virtual. Mixin adalah "kelas virtual" yang telah disuntikkan dalam rantai leluhur kelas atau modul.
Ketika kita menggunakan "include" dan memberikannya sebuah modul, modul itu menambahkan modul ke rantai leluhur tepat sebelum kelas yang kita warisi dari:
Setiap objek di Ruby juga memiliki kelas tunggal. Metode yang ditambahkan ke kelas singleton ini dapat langsung dipanggil pada objek dan karenanya mereka bertindak sebagai metode "kelas". Ketika kita menggunakan "extended" pada objek dan meneruskan objek modul, kami menambahkan metode modul ke kelas tunggal objek:
Kita dapat mengakses kelas singleton dengan metode singleton_class:
Ruby menyediakan beberapa kait untuk modul ketika mereka dicampur ke dalam kelas / modul.
included
adalah metode kait yang disediakan oleh Ruby yang dipanggil setiap kali Anda memasukkan modul dalam beberapa modul atau kelas. Sama seperti yang disertakan, adaextended
kait terkait untuk memperpanjang. Ini akan dipanggil ketika modul diperluas oleh modul atau kelas lain.Ini menciptakan pola menarik yang dapat digunakan pengembang:
Seperti yang Anda lihat, modul tunggal ini menambahkan metode instance, metode "class", dan bertindak langsung pada kelas target (memanggil a_class_method () dalam kasus ini).
ActiveSupport :: Concern merangkum pola ini. Berikut modul yang sama ditulis ulang untuk menggunakan ActiveSupport :: Concern:
sumber
Saat ini, saya sedang memikirkan
template
pola desain. Itu tidak akan terasa benar dengan modul.sumber