Warisan salah

12

Saya memiliki beberapa kode di mana model pewarisan yang baik telah menurun dan saya mencoba memahami mengapa dan bagaimana cara memperbaikinya. Pada dasarnya, bayangkan Anda memiliki hierarki Kebun Binatang dengan:

class Animal  
class Parrot : Animal 
class Elephant : Animal 
class Cow : Animal

dll.

Anda memiliki metode eat (), run (), dll, dan semuanya baik-baik saja. Kemudian suatu hari seseorang datang dan berkata - kelas CageBuilder kami berfungsi dengan baik dan menggunakan animal.weight () dan animal.height (), kecuali untuk Bison Afrika baru yang terlalu kuat dan dapat menghancurkan dinding, jadi saya akan menambahkan satu lagi properti ke kelas Animal - isAfricanBizon () dan menggunakannya saat memilih materi dan hanya menimpanya untuk kelas AfricanBizon. Orang berikutnya datang dan melakukan sesuatu yang serupa dan selanjutnya Anda tahu Anda memiliki semua properti ini khusus untuk beberapa subset hierarki ke dalam kelas dasar.

Apa cara yang baik untuk meningkatkan / memperbaiki kode semacam itu? Salah satu alternatif di sini adalah dengan hanya menggunakan dynamic_casts untuk memeriksa jenis tetapi itu mengacaukan penelepon dan menambahkan sekelompok if-then-else di semua tempat. Anda dapat memiliki antarmuka yang lebih spesifik di sini tetapi jika semua yang Anda miliki adalah referensi kelas dasar yang tidak banyak membantu. Ada saran lain? Contohnya?

Terima kasih!

naumcho
sumber
@ James: maka Anda harus menulis parser dengan tangan. : S
Matteo Italia
5
Jelas ini adalah kasus kebutuhan pelanggan yang konyol. Tidak ada bison di Afrika. Anda tidak dapat mendesain model objek yang tidak memiliki koneksi dengan kenyataan. Kecuali kenyataan itu diciptakan oleh tangan yang penuh dengan dolar. Yang memecahkan masalah.
Hans Passant
1
Makan semua bison? [Saya memposting ini sebelumnya tetapi karena alasan tertentu dihapus, mungkin oleh jackasses tanpa humor.]
James McNellis
Apakah CageBuilder membutuhkan kelasnya sendiri? Bagaimana jika ada metode MakeCage default, yang dapat ditimpa oleh setiap kelas individu.
Pekerjaan
1
Anda menyebutkan jika-maka-lain mengacaukan sebagai kelemahan bagi penelepon tetapi segera setelah penelepon mulai menggunakan isAfricanBizon () mereka mengacaukan kode dengan jika-maka-lain secara otomatis. Jadi itu adalah jika-maka-lagi kekacauan dengan isAfricanBizon () atau jika-maka-lain kekacauan dengan gips dinamis.
davidk01

Jawaban:

13

Sepertinya masalahnya adalah alih-alih menerapkan WajibConcreteWall (), mereka menerapkan panggilan bendera IsAfricanBison (), dan kemudian memindahkan logika tentang apakah dinding harus berubah di luar ruang lingkup kelas. Kelas Anda harus memaparkan perilaku dan persyaratan, bukan identitas; konsumen Anda dari kelas-kelas ini harus bekerja berdasarkan apa yang diperintahkan, bukan berdasarkan apa yang mereka katakan.

Josh
sumber
1
-1: Hanya mengatakan apa yang tidak boleh dilakukan. OP sudah tahu bahwa ini adalah ide yang buruk, maka pertanyaannya.
Steven Evers
12

isAfricanBizon () tidak generik. Misalkan Anda memperluas peternakan hewan Anda dengan hyppopotamus yang juga terlalu kuat tetapi mengembalikan true dari isAfricanBizon () untuk memiliki efek yang tepat akan konyol.

Anda selalu ingin menambahkan metode ke antarmuka yang menjawab pertanyaan spesifik, dalam hal ini akan menjadi sesuatu seperti kekuatan ()

Karoly Horvath
sumber
+1: Semua orang tampaknya melanggar model konseptual kelas (yang hanya merangkum sifat berbagai jenis hewan), untuk mengakomodasi kasus penggunaan khusus ini. Sebuah strengthmetode dapat ditanyakan dengan material.canHold(animal), memungkinkan cara yang bersih untuk mendukung berbagai jenis bahan daripada ConcreteWall.
Aidan Cully
Saya suka kekuatan () pendekatan properti lebih baik daripada saran yang lain dari MembutuhkanConcreteWall () karena lebih fleksibel untuk memungkinkan persyaratan di masa depan. Sebagai permulaan, buat kelas CageBuilder memutuskan materi mana yang cukup kuat, dan kemudian Anda dapat dengan mudah memperluas kelas dengan materi baru.
jhocking
3

Saya pikir masalah Anda adalah ini: Anda memiliki berbagai klien perpustakaan yang tertarik hanya sebagian dari hirarki tetapi yang melewati pointer / referensi ke kelas dasar. Itu sebenarnya masalah yang dynamic_cast <> ada untuk dipecahkan.

Ini adalah masalah desain klien untuk meminimalkan penggunaan dynamic_cast <>; mereka harus menggunakannya untuk menentukan apakah objek memerlukan perlakuan khusus dan jika demikian lakukan semua operasi pada referensi yang dicor.

Jika Anda memiliki koleksi fungsi "campuran" yang berlaku untuk beberapa sub hierarki yang terpisah, Anda mungkin ingin menggunakan pola antarmuka yang digunakan Java dan C #; memiliki kelas dasar virtual yang merupakan kelas virtual-murni, dan gunakan dynamic_cast <> untuk menentukan apakah instance menyediakan implementasi untuknya.

antlersoft
sumber
1

Satu hal yang dapat Anda lakukan adalah mengganti pemeriksaan tipe secara eksplisit seperti isAfricanBison()dengan memeriksa properti yang sebenarnya Anda minati, yaitu isTooStrong().

hammar
sumber
1
isTooStrong () untuk apa? Anda menambahkan kode khusus kandang ke kelas hewan.
Steven Evers
1

Hewan seharusnya tidak peduli dengan dinding beton. Mungkin Anda bisa mengungkapkannya dengan nilai-nilai sederhana.

class Animal {
public:
  virtual ~Animal() {}
  virtual size_t height() const = 0;
  virtual size_t weight() const = 0;
  virtual bool isStrong() const = 0;
};

Cage *CreateCageFromSQL(Animal &a);
Cage *CreateCageFromOrangePeelsAndSticks(Animal &a);

Saya kira itu tidak layak. Itulah masalah dengan contoh mainan.

Saya tidak akan pernah ingin melihat MemerlukanConcreteWalls () atau garis dan garis gips pointer dinamis pada tingkat apapun.

Ini biasanya solusi yang murah . Sangat mudah untuk mempertahankan dan membuat konsep. Dan sungguh, masalahnya menyatakan bahwa itu terkait dengan jenis hewan.

class Animal {
public:
  virtual ~Animal() {}
  virtual CageBuilder *getCageBuilder() = 0;
};

Ini tidak menghalangi Anda untuk menggunakan kode bersama, cukup mencemari Hewan sedikit.

Tetapi bagaimana kandang dibangun mungkin merupakan kebijakan dari beberapa sistem lain, dan mungkin Anda memiliki lebih dari satu jenis pembangun kandang per hewan. Ada banyak kombinasi aneh dan berbelit-belit yang bisa Anda buat.

Saya telah menggunakan Desain Berbasis Komponen untuk tujuan yang baik, masalah utamanya adalah bahwa hal itu dapat menyulitkan ketika kepemilikan Hewan dibagikan. Bagaimana menghindari melempar destruktor menjadi titik rasa sakit.

Double Dispatch adalah pilihan lain, meskipun saya selalu ragu untuk menerimanya.

Di luar itu sulit untuk menebak masalahnya.

Tom Kerr
sumber
0

Yah pasti semua Hewan memiliki properti bawaan attemptEscape(). Sementara beberapa metode dapat menimbulkan falsehasil dalam semua skenario sementara yang lain mungkin memiliki kesempatan berdasarkan heuristik karakteristik intrinsik mereka yang lain seperti sizedan weight. Maka tentu saja di beberapa titik attemptEscape()menjadi sepele karena pasti akan kembali true.

Saya khawatir saya tidak sepenuhnya memahami pertanyaan Anda ... semua hewan memiliki tindakan dan karakteristik yang terkait. Yang khusus untuk hewan harus diperkenalkan di tempat yang cocok. Mencoba menghubungkan langsung Bison dengan Parrots bukanlah pengaturan bawaan yang baik dan seharusnya tidak menjadi masalah dalam desain yang tepat.


sumber
-1

Pilihan lain adalah menggunakan pabrik yang membuat kandang yang sesuai untuk setiap hewan. Saya pikir ini bisa lebih baik jika kondisinya sangat berbeda untuk masing-masing. Tetapi jika hanya kondisi ini saja RequiresConcreteWall()metode yang disebutkan di atas akan melakukannya.

RocketR
sumber
-1

bagaimana dengan RecommendCageType () sebagaimana oppsed ke MembutuhkanConcreteWall ()

Orang bodoh
sumber
-2

Kenapa tidak melakukan hal seperti ini

class Animals { /***/ } class HeavyAnimals{} : Animals //The basic class for animals like the African Bison

Dengan kelas HeavyAnimals, Anda dapat membuat kelas African Bison dengan memperluas kelas HeavyAnimals.

Jadi sekarang Anda kelas induk (Hewan) yang dapat digunakan untuk membuat kelas dasar lainnya seperti kelas HeavyAnimal dengan dapat digunakan untuk membuat kelas Bison Afrika dan Hewan Berat lainnya. Jadi dengan Bison Afrika Anda sekarang memiliki akses ke metode dan properti kelas Hewan (ini adalah basis untuk semua hewan) dan akses ke kelas HeavyAnimal (ini adalah dasar untuk Hewan Berat)

mohhamed rafi
sumber
2
Itu mungkin berfungsi sebagai campuran atau sifat, tetapi tentu saja tidak sebagai subkelas. Ini hanya meminta beberapa warisan di lain waktu properti lain diperlukan.
Ordous