Pertanyaan di atas adalah contoh abstrak dari masalah umum yang saya temui dalam kode lama, atau lebih tepatnya, masalah yang dihasilkan dari upaya sebelumnya dalam memecahkan masalah ini.
Saya bisa memikirkan setidaknya satu .NET framework metode yang dimaksudkan untuk mengatasi masalah ini, seperti Enumerable.OfType<T>
metode ini. Tetapi fakta bahwa Anda akhirnya menginterogasi jenis objek saat runtime tidak cocok dengan saya.
Selain bertanya pada setiap kuda, "Apakah Anda unicorn?" pendekatan-pendekatan berikut juga muncul dalam pikiran:
- Melempar pengecualian ketika upaya dilakukan untuk mendapatkan panjang tanduk non-unicorn (memperlihatkan fungsi yang tidak sesuai untuk setiap kuda)
- Mengembalikan nilai default atau magic untuk panjang tanduk non-unicorn (memerlukan cek default yang dibumbui di seluruh kode yang ingin mengelompokkan statistik tanduk pada sekelompok kuda yang semuanya bisa non-unicorn)
- Hapus warisan dan buatlah objek terpisah pada kuda yang memberi tahu Anda apakah kuda itu unicorn atau tidak (yang berpotensi mendorong masalah yang sama ke lapisan bawah)
Saya merasa ini akan menjadi jawaban terbaik dengan "tidak dijawab." Tetapi bagaimana Anda mendekati masalah ini dan jika itu tergantung, bagaimana konteks keputusan Anda?
Saya juga tertarik dengan wawasan tentang apakah masalah ini masih ada dalam kode fungsional (atau mungkin hanya ada dalam bahasa fungsional yang mendukung kemampuan berubah-ubah?)
Ini ditandai sebagai kemungkinan duplikat dari pertanyaan berikut: Bagaimana cara menghindari downcasting?
Jawaban atas pertanyaan itu mengasumsikan bahwa seseorang memiliki a HornMeasurer
dengan mana semua pengukuran tanduk harus dilakukan. Tapi itu cukup memaksakan basis kode yang dibentuk dengan prinsip egaliter bahwa setiap orang harus bebas untuk mengukur tanduk kuda.
Jika tidak ada HornMeasurer
, pendekatan jawaban yang diterima mencerminkan pendekatan berbasis pengecualian yang tercantum di atas.
Ada juga beberapa kebingungan dalam komentar tentang apakah kuda dan unicorn keduanya kuda, atau jika unicorn adalah subspesies kuda ajaib. Kedua kemungkinan harus dipertimbangkan - mungkin yang satu lebih disukai dari yang lain?
sumber
Jawaban:
Dengan asumsi Anda ingin memperlakukan
Unicorn
sebagai jenis khususHorse
, pada dasarnya ada dua cara Anda dapat memodelkannya. Cara yang lebih tradisional adalah hubungan subclass. Anda dapat menghindari memeriksa jenis dan downcasting dengan hanya refactoring kode Anda untuk selalu menjaga daftar terpisah dalam konteks di mana hal itu penting, dan hanya menggabungkannya dalam konteks di mana Anda tidak pernah peduli tentangUnicorn
sifat - sifat. Dengan kata lain, Anda mengaturnya sehingga Anda tidak pernah masuk ke situasi di mana Anda perlu mengekstrak unicorn dari kawanan kuda di tempat pertama. Ini tampaknya sulit pada awalnya, tetapi mungkin dalam 99,99% kasus, dan biasanya benar-benar membuat kode Anda jauh lebih bersih.Cara lain Anda bisa membuat model unicorn hanya dengan memberikan semua kuda panjang tanduk opsional. Kemudian Anda dapat menguji apakah itu unicorn dengan memeriksa apakah ia memiliki panjang tanduk, dan menemukan panjang tanduk rata-rata semua unicorn dengan (dalam Scala):
Metode ini memiliki keuntungan menjadi lebih lugas, dengan satu kelas, tetapi kerugian karena jauh lebih tidak bisa diperluas, dan memiliki semacam cara memutar untuk memeriksa "unicornness." Kiatnya jika Anda menggunakan solusi ini adalah mengenali kapan Anda mulai sering memperluasnya sehingga Anda perlu pindah ke arsitektur yang lebih fleksibel. Solusi semacam ini jauh lebih populer dalam bahasa fungsional di mana Anda memiliki fungsi yang sederhana dan kuat seperti
flatMap
dengan mudah menyaringNone
item.sumber
Horse
memilikiIsUnicorn
properti dan beberapa jenisUnicornStuff
properti dengan panjang tanduk di atasnya (ketika scaling untuk pengendara / glitter yang disebutkan dalam pertanyaan Anda).Anda sudah cukup banyak membahas semua opsi. Jika Anda memiliki perilaku yang bergantung pada subtipe tertentu, dan dicampur dengan tipe lain, kode Anda harus mengetahui subtipe itu; itu alasan logis sederhana.
Secara pribadi, saya hanya pergi dengan
horses.OfType<Unicorn>().Average(u => u.HornLength)
. Ini mengungkapkan maksud dari kode dengan sangat jelas, yang sering kali merupakan hal yang paling penting karena seseorang akhirnya harus memeliharanya nanti.sumber
Unicorn
s saja (untuk catatan Anda bisa menghilangkanreturn
).Tidak ada yang salah dengan .NET dengan:
Menggunakan persamaan Linq juga baik-baik saja:
Berdasarkan pertanyaan yang Anda tanyakan dalam judul, ini adalah kode yang saya harapkan untuk ditemukan. Jika pertanyaannya diajukan seperti "apa rata-rata hewan dengan tanduk" itu akan berbeda:
Perhatikan bahwa ketika menggunakan Linq,
Average
(danMin
danMax
) akan mengeluarkan pengecualian jika enumerable kosong dan tipe T tidak dapat dibatalkan. Itu karena rata-rata benar-benar tidak terdefinisi (0/0). Jadi Anda benar-benar membutuhkan sesuatu seperti ini:Edit
Saya hanya berpikir ini perlu menambahkan ... salah satu alasan pertanyaan seperti ini tidak cocok dengan pemrogram berorientasi objek adalah mengasumsikan kita menggunakan kelas dan objek untuk memodelkan struktur data. Ide berorientasi objek Smalltalk-esque asli adalah untuk menyusun program Anda keluar dari modul yang dipakai sebagai objek dan melakukan layanan untuk Anda ketika Anda mengirim mereka pesan. Fakta bahwa kita juga dapat menggunakan kelas dan objek untuk memodelkan struktur data adalah efek samping (berguna), tetapi mereka adalah dua hal yang berbeda. Saya bahkan tidak berpikir yang terakhir harus dianggap pemrograman berorientasi objek, karena Anda bisa melakukan hal yang sama dengan
struct
, tapi itu tidak akan secantik itu.Jika Anda menggunakan pemrograman berorientasi objek untuk membuat layanan yang melakukan hal-hal untuk Anda, maka menanyakan apakah layanan itu sebenarnya beberapa layanan lain atau implementasi konkret umumnya disukai karena alasan yang baik. Anda diberi antarmuka (biasanya melalui injeksi dependensi) dan Anda harus kode ke antarmuka / kontrak itu.
Di sisi lain, jika Anda (salah-) menggunakan ide-ide kelas / objek / antarmuka untuk membuat struktur data atau model data, maka saya pribadi tidak melihat masalah dengan menggunakan ide is-a secara maksimal. Jika Anda telah menetapkan bahwa unicorn adalah sub-jenis kuda dan itu benar-benar masuk akal dalam domain Anda, maka silakan saja dan minta kuda-kuda dalam kawanan Anda untuk menemukan unicorn. Lagi pula, dalam kasus seperti ini kami biasanya mencoba membuat bahasa khusus domain untuk mengekspresikan solusi dari masalah yang harus kami selesaikan dengan lebih baik. Dalam hal itu tidak ada yang salah dengan
.OfType<Unicorn>()
dll.Pada akhirnya, mengambil koleksi item dan memfilternya pada tipe sebenarnya hanya pemrograman fungsional, bukan pemrograman berorientasi objek. Untungnya bahasa seperti C # nyaman menangani kedua paradigma sekarang.
sumber
animal
adalahUnicorn
; hanya membuang daripada menggunakanas
, atau berpotensi menggunakan lebih baikas
dan kemudian memeriksa nol.Masalah dengan pernyataan ini adalah, tidak peduli mekanisme apa yang Anda gunakan, Anda akan selalu menginterogasi objek untuk mengetahui jenisnya. Itu bisa RTTI atau bisa menjadi gabungan atau struktur data biasa di mana Anda bertanya
if horn > 0
. Spesifik persis berubah sedikit tetapi tujuannya sama - Anda bertanya objek tentang dirinya sendiri dalam beberapa cara untuk melihat apakah Anda harus menginterogasinya lebih lanjut.Karena itu, masuk akal untuk menggunakan dukungan bahasa Anda untuk melakukan ini. Di .NET Anda akan menggunakan
typeof
misalnya.Alasan untuk melakukan ini melampaui hanya menggunakan bahasa Anda dengan baik. Jika Anda memiliki objek yang terlihat seperti yang lain tetapi untuk beberapa perubahan kecil, kemungkinan Anda akan menemukan lebih banyak perbedaan dari waktu ke waktu. Dalam contoh unicorn / kuda Anda, Anda mungkin mengatakan hanya panjang tanduk ... tapi besok Anda akan memeriksa untuk melihat apakah pengendara potensial adalah perawan, atau jika kotorannya berkilauan. (contoh dunia nyata klasik adalah widget GUI yang berasal dari basis umum dan Anda harus mencari kotak centang dan kotak daftar secara berbeda. Jumlah perbedaan akan terlalu besar untuk hanya membuat objek super tunggal yang menampung semua kemungkinan permutasi data ).
Jika memeriksa jenis objek saat runtime tidak berlaku, maka alternatif Anda adalah dengan membagi objek yang berbeda sejak awal - alih-alih menyimpan kawanan unicorn / kuda, Anda memegang 2 koleksi - satu untuk kuda, satu untuk unicorn . Ini dapat bekerja dengan sangat baik, bahkan jika Anda menyimpannya dalam wadah khusus (misalnya multimap di mana kuncinya adalah jenis objek ... tapi kemudian meskipun kami menyimpannya dalam 2 kelompok, kami segera kembali menginterogasi jenis objek !)
Tentunya pendekatan berbasis pengecualian salah. Menggunakan pengecualian sebagai aliran program normal adalah bau kode (jika Anda memiliki kawanan unicorn dan keledai dengan kerang yang ditempelkan di kepalanya, maka saya akan mengatakan pendekatan berbasis pengecualian tidak masalah, tetapi jika Anda memiliki kawanan unicorn dan kuda kemudian memeriksa masing-masing unicorn-ness tidak terduga. Pengecualian adalah untuk keadaan luar biasa, bukan
if
pernyataan yang rumit ). Bagaimanapun, menggunakan pengecualian untuk masalah ini hanya menginterogasi tipe objek saat runtime, hanya di sini Anda menyalahgunakan fitur bahasa untuk memeriksa objek non-unicorn. Anda mungkin juga kode dalamif horn > 0
dan setidaknya memproses koleksi Anda dengan cepat, jelas, menggunakan lebih sedikit baris kode dan menghindari masalah yang muncul sejak pengecualian lain dilemparkan (mis. koleksi kosong, atau mencoba mengukur kerang keledai itu)sumber
if horn > 0
cukup banyak cara masalah ini diselesaikan pada awalnya. Maka masalah yang biasanya muncul adalah ketika Anda ingin memeriksa pengendara dan kilau, danhorn > 0
dikubur di semua tempat dalam kode yang tidak terkait (juga kode menderita bug misteri karena kurangnya pemeriksaan ketika tanduk 0). Juga, mengelompokkan kuda berdasarkan fakta biasanya merupakan proposisi paling mahal, jadi saya biasanya tidak cenderung melakukannya jika mereka masih menulis bersama di akhir refactor. Jadi itu tentu saja menjadi "betapa jeleknya alternatifnya"Karena pertanyaan memiliki
functional-programming
tag, kita bisa menggunakan tipe penjumlahan untuk mencerminkan dua rasa kuda dan pencocokan pola untuk membedakan keduanya. Misalnya, dalam F #:Lebih dari OOP, FP memiliki keunggulan pemisahan data / fungsi, yang mungkin menyelamatkan Anda dari "nurani bersalah" (yang tidak dibenarkan?) Karena melanggar tingkat abstraksi saat downcasting ke subtipe tertentu dari daftar objek dari supertype.
Berbeda dengan solusi OO yang diusulkan dalam jawaban lain, pencocokan pola juga memberikan titik perpanjangan yang lebih mudah jika spesies Horned lain
Equine
muncul suatu hari.sumber
Bentuk pendek dari jawaban yang sama di akhir membutuhkan membaca buku atau artikel web.
Pola Pengunjung
Masalahnya memiliki campuran kuda dan unicorn. (Melanggar prinsip substitusi Liskov adalah masalah umum dalam basis kode warisan.)
Tambahkan metode ke kuda dan semua subclass
Antar muka pengunjung kuda terlihat seperti ini di java / c #
Untuk mengukur tanduk kita sekarang menulis ....
Pola pengunjung dikritik karena membuat refactoring dan pertumbuhan lebih sulit.
Jawaban Singkat: Gunakan pola desain Pengunjung untuk mendapatkan pengiriman ganda.
lihat juga https://en.wikipedia.org/wiki/Visitor_pattern
lihat juga http://c2.com/cgi/wiki?VisitorPattern untuk diskusi tentang pengunjung.
lihat juga Pola Desain oleh Gamma et al.
sumber
Dengan asumsi bahwa dalam arsitektur Anda unicorn adalah subspesies kuda dan Anda menemukan tempat di mana Anda mendapatkan koleksi di
Horse
mana beberapa dari mereka mungkinUnicorn
, saya pribadi akan pergi dengan metode pertama (.OfType<Unicorn>()...
) karena itu adalah cara paling mudah untuk mengekspresikan niat Anda . Bagi siapa saja yang datang kemudian (termasuk diri Anda dalam 3 bulan), segera jelas apa yang ingin Anda capai dengan kode itu: pilih unicorn dari antara kuda-kuda.Metode lain yang Anda daftarkan terasa seperti cara lain untuk mengajukan pertanyaan "Apakah Anda unicorn?". Misalnya, jika Anda menggunakan semacam metode berbasis pengecualian untuk mengukur tanduk, Anda mungkin memiliki kode yang akan terlihat seperti ini:
Jadi sekarang pengecualian menjadi indikator bahwa sesuatu bukan unicorn. Dan sekarang ini bukan situasi yang luar biasa lagi, tetapi merupakan bagian dari aliran program normal. Dan menggunakan pengecualian bukannya
if
tampak lebih kotor daripada hanya melakukan pemeriksaan tipe.Katakanlah Anda pergi rute nilai ajaib untuk memeriksa tanduk pada kuda. Jadi sekarang kelas Anda terlihat seperti ini:
Sekarang
Horse
kelas Anda harus tahu tentangUnicorn
kelas dan memiliki metode tambahan untuk menangani hal-hal yang tidak dipedulikannya. Sekarang bayangkan Anda juga memilikiPegasus
danZebra
s yang mewarisi dariHorse
. SekarangHorse
membutuhkanFly
metode sertaMeasureWings
,CountStripes
, dll Dan kemudianUnicorn
kelas mendapat metode ini juga. Sekarang semua kelas Anda harus tahu tentang satu sama lain dan Anda telah mencemari kelas dengan banyak metode yang seharusnya tidak ada di sana hanya untuk menghindari bertanya pada sistem tipe "Apakah ini unicorn?"Jadi bagaimana dengan menambahkan sesuatu untuk
Horse
dikatakan jika ada sesuatuUnicorn
dan menangani semua pengukuran tanduk? Nah, sekarang Anda harus memeriksa keberadaan objek ini untuk mengetahui apakah ada sesuatu yang unicorn (yang hanya menggantikan satu cek dengan yang lain). Ini juga mengeruhkan air sedikit di bahwa sekarang Anda mungkin memilikiList<Horse> unicorns
yang benar-benar menampung semua unicorn, tetapi sistem tipe dan debugger tidak dapat dengan mudah mengatakan itu. "Tapi aku tahu itu semua unicorn," katamu, "nama itu bahkan berkata begitu." Nah, bagaimana jika ada sesuatu yang tidak disebutkan namanya? Atau katakan, Anda menulis sesuatu dengan asumsi bahwa itu benar-benar semua unicorn, tetapi kemudian persyaratan berubah dan sekarang mungkin juga ada pegasi yang tercampur? (Karena hal seperti ini tidak pernah terjadi, terutama dalam perangkat lunak warisan / sarkasme.) Sekarang sistem jenis akan dengan senang hati menempatkan pegasi Anda dengan unicorn Anda. Jika variabel Anda telah dinyatakan sebagaiList<Unicorn>
kompiler (atau lingkungan runtime) akan cocok jika Anda mencoba untuk mencampur pegasi atau kuda.Akhirnya, semua metode ini hanyalah pengganti untuk pemeriksaan jenis sistem. Secara pribadi, saya lebih suka tidak menemukan kembali roda di sini dan berharap bahwa kode saya berfungsi sama baiknya dengan sesuatu yang ada di dalamnya dan telah diuji oleh ribuan coders lainnya ribuan kali.
Pada akhirnya, kode harus dapat dimengerti oleh Anda . Komputer akan mengetahuinya terlepas dari bagaimana Anda menulisnya. Anda adalah orang yang harus men-debug dan dapat mempertimbangkannya. Tentukan pilihan yang membuat pekerjaan Anda lebih mudah. Jika karena alasan tertentu, salah satu metode lain ini menawarkan keuntungan bagi Anda yang lebih besar daripada kode yang lebih jelas di beberapa tempat yang akan ditampilkan, lakukan saja. Tetapi itu tergantung pada basis kode Anda.
sumber
if(horse.IsUnicorn) horse.MeasureHorn();
dan pengecualian tidak akan tertangkap - mereka akan dipicu kapan!horse.IsUnicorn
dan Anda berada dalam konteks pengukuran unicorn, atau di dalamMeasureHorn
pada non-unicorn. Dengan begitu ketika pengecualian dilemparkan Anda tidak menutupi kesalahan, itu benar-benar meledak dan merupakan pertanda sesuatu perlu diperbaiki. Jelas itu hanya sesuai untuk skenario tertentu, tetapi ini adalah implementasi yang tidak menggunakan melempar pengecualian untuk menentukan jalur eksekusi.Yah, sepertinya domain semantik Anda memiliki hubungan IS-A, tetapi Anda agak waspada menggunakan subtipe / pewarisan untuk memodelkan ini — terutama karena pantulan tipe runtime. Namun saya pikir Anda takut pada hal yang salah — subtyping memang datang dengan bahaya, tetapi fakta bahwa Anda menanyakan objek saat runtime bukanlah masalahnya. Anda akan melihat apa yang saya maksud.
Pemrograman berorientasi objek telah sangat bergantung pada gagasan hubungan IS-A, itu bisa dibilang terlalu condong padanya, mengarah ke dua konsep kritis terkenal:
Tapi saya pikir ada cara lain yang lebih fungsional berbasis pemrograman untuk melihat hubungan IS-A yang mungkin tidak mengalami kesulitan ini. Pertama, kami ingin memodelkan kuda dan unicorn dalam program kami, jadi kami akan memiliki
Horse
danUnicorn
tipe. Apa nilai dari tipe-tipe ini? Baiklah, saya akan mengatakan ini:Itu mungkin terdengar jelas, tetapi saya pikir salah satu cara orang masuk ke masalah seperti masalah lingkaran-elips adalah dengan tidak memikirkan poin-poin itu dengan cukup hati-hati. Setiap lingkaran adalah elips, tetapi itu tidak berarti bahwa setiap uraian lingkaran yang terencana secara otomatis merupakan uraian terencana dari elips menurut skema yang berbeda. Dengan kata lain, hanya karena lingkaran adalah elips tidak berarti bahwa a
Circle
adalahEllipse
, jadi untuk berbicara. Tetapi itu berarti:Circle
(deskripsi lingkaran terencana) menjadiEllipse
(berbagai jenis deskripsi) yang menggambarkan lingkaran yang sama;Ellipse
dan, jika menggambarkan lingkaran, mengembalikan yang sesuaiCircle
.Jadi, dalam istilah pemrograman fungsional,
Unicorn
tipe Anda tidak perlu menjadi subtipeHorse
sama sekali, Anda hanya perlu operasi seperti ini:Dan
toUnicorn
perlu kebalikan daritoHorse
:Maybe
Jenis Haskell adalah bahasa lain yang disebut jenis "opsi". Misalnya, tipe Java 8Optional<Unicorn>
adalah anUnicorn
atau tidak sama sekali. Perhatikan bahwa dua alternatif Anda — melempar pengecualian atau mengembalikan "nilai default atau sihir" - sangat mirip dengan jenis opsi.Jadi pada dasarnya yang saya lakukan di sini adalah merekonstruksi konsep hubungan IS-A dalam hal jenis dan fungsi, tanpa menggunakan subtipe atau warisan. Apa yang akan saya ambil dari ini adalah:
Horse
tipe;Horse
jenis kebutuhan untuk mengkodekan informasi yang cukup untuk menentukan dengan pasti apakah nilai apapun menggambarkan unicorn;Horse
tipe perlu memaparkan informasi tersebut sehingga klien tipe dapat mengamati apakah diberikanHorse
adalah unicorn;Horse
jenis ini harus menggunakan operasi terakhir ini pada saat runtime untuk membedakan antara unicorn dan kuda.Jadi ini pada dasarnya adalah "tanya setiap
Horse
apakah itu unicorn". Anda waspada dengan model itu, tapi saya pikir salah. Jika saya memberi Anda daftarHorse
s, semua yang dijamin oleh tipenya adalah bahwa hal-hal yang dideskripsikan oleh item dalam daftar adalah kuda — jadi Anda, mau tidak mau, akan perlu melakukan sesuatu saat runtime untuk memberi tahu mereka yang unicorn. Jadi saya kira tidak ada jalan keluarnya - Anda perlu mengimplementasikan operasi yang akan melakukannya untuk Anda.Dalam pemrograman berorientasi objek, cara yang biasa dilakukan adalah sebagai berikut:
Horse
tipe;Unicorn
sebagai subtipe dariHorse
;Horse
adalahUnicorn
.Ini memang memiliki kelemahan besar, ketika Anda melihatnya dari sudut "hal vs deskripsi" yang saya sajikan di atas:
Horse
instance yang menggambarkan unicorn tetapi bukan sebuahUnicorn
instance?Kembali ke awal, inilah yang saya pikir adalah bagian yang sangat menakutkan tentang menggunakan subtyping dan downcast untuk memodelkan hubungan IS-A ini — bukan fakta bahwa Anda harus melakukan pemeriksaan runtime. Menyalahgunakan tipografi sedikit, menanyakan
Horse
apakah ini sebuahUnicorn
instance tidak sama dengan menanyakanHorse
apakah itu unicorn (apakah itu adalah-Horse
deskripsi kuda yang juga unicorn). Tidak, kecuali jika program Anda telah berusaha keras untuk merangkum kode yang membangunHorses
sehingga setiap kali klien mencoba untuk membangunHorse
yang menggambarkan unicorn,Unicorn
kelas akan dipakai. Dalam pengalaman saya, jarang programmer melakukan hal ini dengan hati-hati.Jadi saya akan pergi dengan pendekatan di mana ada operasi eksplisit, non-downcast yang mengubah
Horse
s keUnicorn
s. Ini bisa berupa metodeHorse
tipe:... atau itu bisa menjadi objek eksternal ("objek terpisah Anda pada kuda yang memberi tahu Anda apakah kuda itu unicorn atau tidak"):
Pilihan di antara ini adalah masalah bagaimana program Anda diatur — dalam kedua kasus, Anda memiliki operasi yang setara dengan saya di
Horse -> Maybe Unicorn
atas, Anda hanya mengemasnya dengan cara yang berbeda (yang tentu saja akan memiliki efek riak pada operasi apa yangHorse
dibutuhkan tipe tersebut. untuk mengekspos kepada kliennya).sumber
Komentar OP dalam jawaban lain menjelaskan pertanyaan itu, pikir saya
Frasa seperti itu, saya pikir kita perlu lebih banyak informasi. Jawabannya mungkin tergantung pada beberapa hal:
herd.averageHornLength()
tampaknya cocok dengan model konseptual kami.Namun secara umum, saya bahkan tidak akan berpikir tentang warisan dan subtipe di sini. Anda memiliki daftar objek. Beberapa objek tersebut dapat diidentifikasi sebagai unicorn, mungkin karena mereka memiliki a
hornLength()
metode. Saring daftar berdasarkan properti unik-unicorn ini. Sekarang masalahnya telah dikurangi menjadi rata-rata panjang tanduk dari daftar unicorn.OP, beri tahu saya jika saya masih salah paham ...
sumber
HerdMember
) yang kita inisialisasi dengan kuda atau unicorn (membebaskan kuda dan unicorn dari memerlukan hubungan subtipe) ).HerdMember
kemudian bebas untuk mengimplementasikanisUnicorn()
namun itu sesuai, dan solusi penyaringan saran saya berikut.Metode GetUnicorns () yang mengembalikan IEnumerable tampaknya solusi yang paling elegan, fleksibel dan universal bagi saya. Dengan cara ini Anda bisa berurusan dengan (kombinasi dari) sifat-sifat yang menentukan apakah seekor kuda akan lulus sebagai unicorn, bukan hanya tipe kelas atau nilai properti tertentu.
sumber
horses.ofType<Unicorn>...
konstruksi. MemilikiGetUnicorns
fungsi akan menjadi one-liner, tetapi akan lebih tahan terhadap perubahan pada hubungan kuda / unicorn dari perspektif pemanggil.IEnumerable<Horse>
, meskipun kriteria unicorn Anda ada di satu tempat, itu dirangkum, jadi penelepon Anda harus membuat asumsi tentang mengapa mereka memerlukan unicorn (saya bisa mendapatkan clam chowder dengan memesan sup hari ini, tapi itu tidak ' t berarti saya akan mendapatkannya besok dengan melakukan hal yang sama). Plus, Anda harus mengekspos nilai default untuk klakson padaHorse
. JikaUnicorn
jenisnya sendiri, Anda harus membuat tipe baru dan mempertahankan pemetaan tipe, yang dapat menyebabkan overhead.