Desain Berorientasi Objek

23

Misalkan Anda memiliki yang berikut:

     +--------+     +------+
     | Animal |     | Food |
     +-+------+     +----+-+
       ^                 ^
       |                 |
       |                 |
  +------+              +-------+
  | Deer |              | Grass |
  +------+              +-------+

Deermewarisi dari Animal, dan Grassmewarisi dari Food.

Sejauh ini baik. Animalbenda bisa memakan Foodbenda.

Sekarang mari kita campur sedikit. Mari kita tambahkan Lionyang mewarisi dari Animal.

     +--------+     +------+
     | Animal |     | Food |
     +-+-----++     +----+-+
       ^     ^           ^
       |     |           |
       |     |           |
  +------+ +------+     +-------+
  | Deer | | Lion |     | Grass |
  +------+ +------+     +-------+

Sekarang kita punya masalah karena Lionbisa makan keduanya Deerdan Grass, tetapi Deerbukan Fooditu masalahnya Animal.

Tanpa menggunakan pewarisan berganda, dan menggunakan desain berorientasi objek, bagaimana Anda mengatasi masalah ini?

FYI: Saya menggunakan http://www.asciiflow.com untuk membuat diagram ASCII.

Michael Irey
sumber
14
Membuat model dunia nyata biasanya menjadi masalah cepat atau lambat, karena selalu ada sesuatu yang aneh terjadi (seperti ikan terbang, ikan atau burung? Tetapi seekor penguin adalah burung, tidak bisa terbang dan makan ikan). Apa yang dikatakan @Ampt terdengar masuk akal, Hewan harus memiliki koleksi makanan yang dimakannya.
Rob van der Veer
2
Saya pikir Hewan harus mewarisi dari Makanan. Jika sesuatu mencoba memakan Singa, cukup lemparkan InvalidOperationException.
RalphChapin
4
@RalphChapin: Semua jenis memakan singa (burung nasar, serangga, dll). Saya pikir hewan dan makanan adalah perbedaan artifisial yang akan rusak karena mereka tidak cukup luas (pada akhirnya semua hewan adalah makanan hewan lainnya). Jika Anda masuk dalam kategori "LivingThing", Anda hanya perlu berurusan dengan kasing tepi dengan tanaman yang memakan makhluk tidak hidup (mineral, dll.), Dan tidak akan merusak apa pun untuk memiliki LivingThing.Eat (LivingThing).
Satanicpuppy
2
Berbagi penelitian Anda membantu semua orang. Beri tahu kami apa yang telah Anda coba dan mengapa itu tidak memenuhi kebutuhan Anda. Ini menunjukkan bahwa Anda telah meluangkan waktu untuk mencoba membantu diri sendiri, itu menyelamatkan kami dari mengulangi jawaban yang jelas, dan yang paling utama itu membantu Anda mendapatkan jawaban yang lebih spesifik dan relevan. Lihat juga Cara Meminta
nyamuk
9
Pertanyaan ini telah dijawab oleh game Age of Empire III. ageofempires.wikia.com/wiki/List_of_Animals mengimplementasikan Deer dan Gazelle IHuntable, Sheep and Cow IHerdable(dapat dikontrol oleh manusia), dan Lion hanya mengimplementasikan IAnimal, yang tidak menyiratkan antarmuka mana pun. AOE3 mendukung permintaan seperangkat antarmuka yang didukung oleh objek tertentu (mirip dengan instanceof) yang memungkinkan program untuk menanyakan kemampuannya.
rwong

Jawaban:

38

IS A relationship = Inheritance

Singa adalah binatang

HAS A hubungan = Komposisi

Mobil memiliki roda

CAN DO relationship = Antarmuka

Saya bisa makan

Robert Harvey
sumber
5
+1 Itu sangat sederhana namun merupakan ringkasan yang bagus dari 3 tipe hubungan yang berbeda
dreza
4
Alternatif: ICanBeEatenatauIEdible
Mike Weller
2
Hubungan CAN HAZ = lolcats
Steven A. Lowe
1
Bagaimana ini menjawab pertanyaan?
user253751
13

OO hanyalah sebuah metafora yang membentuk dirinya sendiri setelah dunia nyata. Tetapi metafora hanya berjalan sejauh ini.

Biasanya tidak ada cara yang tepat untuk memodelkan sesuatu di OO. Ada cara yang tepat untuk melakukannya untuk masalah tertentu di domain tertentu dan Anda seharusnya tidak berharap itu berfungsi dengan baik jika Anda mengubah masalah Anda, bahkan jika objek domain sama.

Saya pikir ini adalah kesalahpahaman paling umum Comp. Eng siswa di tahun-tahun pertama mereka. OO bukan solusi universal, hanya alat yang layak untuk beberapa jenis masalah yang dapat memodelkan domain Anda dengan cukup baik.

Saya tidak menjawab pertanyaan, justru karena kami kekurangan info domain. Tetapi dengan pemikiran di atas Anda mungkin dapat merancang sesuatu yang sesuai dengan kebutuhan Anda.

DPM
sumber
3
+1 OO adalah alat, bukan agama.
mouviciel
Saya setuju, mungkin tidak ada solusi sempurna jika masalah ini terus berubah dan berkembang. Dalam kondisi saat ini, apakah masalah ini kekurangan info domain untuk menghasilkan solusi?
Michael Irey
Apakah Anda serius berpikir dunia nyata sedang dimodelkan dalam OP? Model hubungan direpresentasikan melalui contoh.
Basilevs
@ Basilev Itu semacam implikasi, sebenarnya, karena ia menyebutkan bagaimana hewan berperilaku dalam kehidupan nyata. Orang perlu peduli dengan mengapa orang perlu perilaku itu diperhitungkan dalam program, IMO. Yang mengatakan, akan menyenangkan bagi saya untuk menyarankan beberapa desain yang mungkin.
DPM
10

Anda ingin memecah hewan lebih lanjut ke dalam sub-kelas mereka (atau setidaknya sejauh masuk akal untuk apa yang Anda lakukan). Mengingat Anda bekerja dengan apa yang tampak seperti hewan biasa dan dua jenis makanan (tanaman dan daging), masuk akal untuk menggunakan karnivora dan herbivora untuk lebih mendefinisikan seekor hewan dan membuatnya terpisah. Inilah yang saya buat untuk Anda.

             +----------------+                   +--------------------+
             |    Animal      |                   |      Food          |
             |----------------|<--+Interfaces+--->|--------------------|
             |                |                   |                    |
             +----------------+                   +--------------------+
                +           +                       +                 +
                |           |    Abstract Classes   |                 |
                |           |        |          |   |                 |
                v           v        v          v   v                 v
   +-----------------+  +----------------+     +------------+      +------------+
   |   Herbivore     |  |  Carnivore     |     |   Plant    |      |   Meat     |
   |-----------------|  |----------------|     |------------|      |------------|
   |Eat(Plant p)     |  |Eat(Meat m)     |     |            |      |            |
   |                 |  |                |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
            +                    +                    +                   +
            |                    |                    |                   |
            v                    v                    v                   v
   +-----------------+  +----------------+     +------------+      +------------+
   |  Deer           |  |   Lion         |     |  Grass     |      |  DeerMeat  |
   |-----------------|  |----------------|     |------------|      |------------|
   |DeerMeat Die()      |void Kill(Deer) |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
                                 ^                    ^
                                 |                    |
                                 |                    |
                              Concrete Classes -------+

Seperti yang Anda lihat, mereka berdua mengekspos metode makan, tetapi apa yang mereka makan berubah. Singa sekarang dapat membunuh rusa, rusa dapat mati dan mengembalikan DeerMeat, dan pertanyaan awal OPs tentang bagaimana memungkinkan singa makan rusa tetapi tidak rumput dijawab tanpa rekayasa seluruh ekosistem.

Tentu saja, ini menjadi menarik dengan sangat cepat karena seekor Rusa dapat dianggap sebagai jenis daging juga, tetapi untuk menjaga hal-hal sederhana, saya akan membuat metode yang disebut kill () di bawah rusa, yang mengembalikan daging rusa, dan menempatkannya sebagai kelas beton memperpanjang daging.

Ampt
sumber
Apakah Deer akan mengekspos antarmuka IMeat?
Dan Pichelman
Daging bukan antarmuka, ini kelas abstrak. Saya menambahkan bagaimana saya akan mengimplementasikannya untuk Anda
Ampt
Eat(Plant p)dan Eat(Meat m)keduanya melanggar LSP.
Tulains Córdova
Bagaimana demikian @ user61852? Saya sengaja tidak mengekspos Eat di antarmuka hewan sehingga setiap jenis hewan dapat memiliki metode makan sendiri.
Ampt
1
TCWL (Terlalu rumit, akan bocor). Masalahnya terdistribusi dan muncul dan solusi Anda statis, terpusat, dan ditentukan sebelumnya. TCWL.
Tulains Córdova
7

Desain saya akan seperti ini:

  1. Makanan dinyatakan sebagai antarmuka; ada antarmuka IFood dan dua antarmuka turunan darinya: IMeat dan IVegetable
  2. Hewan menerapkan IMeat dan Sayuran menerapkan IVegetable
  3. Hewan memiliki dua keturunan, Karnivora dan Hebivora
  4. Karnivora memiliki metode Makan yang menerima instance IMeat
  5. Herbivora memiliki metode Makan yang menerima turunan dari IVegetable
  6. Lion turun dari Carnivore
  7. Rusa turun dari Herbivore
  8. Rumput turun dari Sayuran

Karena Hewan menerapkan IMeat dan Rusa adalah Hewan (Herbivora), Singa, yang merupakan (Karnivora) Hewan yang dapat makan IMeat juga dapat memakan Rusa.

Rusa adalah Herbivora, sehingga ia bisa makan Rumput karena mengimplementasikan IVegetable.

Karnivora tidak bisa makan IVegeable dan Herbivora tidak bisa makan.

AlexSC
sumber
1
Saya melihat banyak jenis enumerasi di sini menggunakan pewarisan hanya untuk membatasi ketika jenis yang diwariskan tidak menerapkan apa pun ... Setiap kali Anda menemukan diri Anda membuat jenis yang tidak menerapkan fungsi apa pun, itu adalah sesuatu yang gratis; Anda telah memperluas model dalam sistem tipe yang tidak menghasilkan nilai terhadap kegunaan dalam kode
Jimmy Hoffa
Ingat dari pada omnivora yang ada, seperti manusia, kera dan beruang.
Tulains Córdova
Jadi bagaimana Anda menambahkan bahwa keduanya, singa dan rusa, adalah mamalia? :-)
johannes
2
@JimmyHoffa Itu disebut "antarmuka penanda" dan penggunaan antarmuka yang benar-benar valid. Perlu ditinjau ulang kode untuk memutuskan apakah penggunaan itu dibenarkan, tetapi ada banyak kasus penggunaan (seperti ini, di mana singa yang mencoba makan Rumput akan melemparkan pengecualian NoInterface). Antarmuka penanda (atau kurangnya) berfungsi untuk meramalkan pengecualian yang akan dilemparkan jika suatu metode dipanggil dengan argumen yang tidak didukung.
rwong
1
@rwong Saya mengerti konsepnya, tidak pernah mendengarnya diformalkan sebelumnya; hanya pengalaman saya telah setiap kali basis kode saya telah bekerja di dalamnya membuat itu hal-hal yang lebih kompleks dan sulit untuk dipelihara. Mungkin pengalaman saya hanyalah di mana orang menggunakannya salah.
Jimmy Hoffa
5

Makanan apa yang bisa dimakan hewan tidak benar-benar membentuk hierarki, dalam hal ini alam gagal untuk menyesuaikan dengan pemodelan berorientasi objek sederhana (perhatikan bahwa meskipun demikian, hewan harus mewarisi dari makanan, karena itu adalah makanan).

Pengetahuan tentang makanan apa yang hewan bisa makan tidak bisa hidup sepenuhnya dengan salah satu kelas, sehingga hanya memiliki referensi ke beberapa anggota dari hirarki makanan tidak bisa cukup untuk memberi tahu Anda apa hal yang dapat Anda makan.

Itu hubungan banyak ke banyak. Ini berarti setiap kali Anda menambahkan hewan, Anda perlu mencari tahu apa yang bisa dimakannya, dan setiap kali Anda menambahkan makanan, Anda perlu mencari tahu apa yang bisa memakannya. Apakah ada struktur lebih lanjut untuk dieksploitasi tergantung pada hewan dan makanan apa yang Anda modelkan.

Berbagai warisan tidak benar-benar menyelesaikan masalah ini dengan baik. Anda membutuhkan semacam koleksi hal-hal yang bisa dimakan oleh hewan, atau hewan yang bisa memakan makanan.

psr
sumber
Seperti yang mereka katakan tentang regex "Saya punya masalah jadi saya digunakan regex, sekarang saya memiliki dua masalah", MI adalah sepanjang baris "Saya punya masalah jadi saya menggunakan MI, sekarang saya memiliki 99 masalah" Jika aku jadi kau aku' d ikuti sia-sia bahwa Anda menyodok di sini meskipun makanan tahu apa yang bisa memakannya, ini sebenarnya menyederhanakan model satu ton. Ketergantungan inversi FTW.
Jimmy Hoffa
1

Saya akan mendekati masalah dari sisi yang berbeda: OOP adalah tentang perilaku. Dalam kasus Anda, apakah Grassmemiliki perilaku untuk menjadi anak Food? Jadi dalam kasus Anda, tidak akan ada Grasskelas, atau setidaknya, itu tidak akan diwarisi dari Food. Juga, jika Anda perlu menegakkan siapa yang bisa makan apa pada waktu kompilasi, patut dipertanyakan apakah Anda perlu Animalabstraksi. Juga, tidak jarang melihat karnivora makan rumput , meskipun bukan untuk makanan.

Jadi saya akan merancang ini sebagai (tidak akan repot dengan seni ASCI):

IEdibledengan properti Type, yang merupakan enum dari daging, tanaman, bangkai, dll. (ini tidak akan sering berubah dan tidak memiliki perilaku tertentu, oleh karena itu tidak perlu memodelkan ini sebagai kelas hiearchy).

Animaldengan metode CanEat(IEdible food)dan Eat(IEdible food), yang logis. Kemudian, hewan tertentu dapat memeriksa kapan saja kemudian bisa makan makanan yang diberikan dalam keadaan tertentu dan kemudian makan makanan itu untuk mendapatkan rezeki / melakukan sesuatu yang lain. Juga, saya akan memodelkan kelas-kelas Karnivora, Herbivora, Omnivora sebagai pola Strategi , daripada sebagai bagian dari hierarki hewan.

Euforia
sumber
1

TL; DR: Desain atau model dengan konteks.

Saya pikir pertanyaan Anda sulit karena tidak memiliki konteks masalah aktual yang Anda coba selesaikan. Anda memiliki beberapa model dan beberapa hubungan, tetapi tidak memiliki kerangka kerja yang harus digunakan. Tanpa konteks, pemodelan dan metafora tidak berfungsi dengan baik membiarkan pintu terbuka untuk banyak interpretasi.

Saya pikir lebih produktif untuk fokus pada bagaimana data akan dikonsumsi. Setelah Anda memiliki pola penggunaan data, lebih mudah untuk bekerja mundur seperti apa model dan hubungan yang seharusnya.

Misalnya persyaratan yang lebih rinci akan memerlukan hubungan objek yang berbeda:

  • mendukung Animals eattidak FoodsukaGastroliths
  • mendukung Chocolatesebagai Poisonuntuk Dogs, tetapi tidak untukHumans

Jika kita memulai latihan tentang bagaimana memodelkan hubungan sederhana yang disajikan, Food Interface mungkin yang terbaik; dan jika itu jumlah keseluruhan bagaimana hubungan dalam sistem maka Anda baik-baik saja. Namun, hanya beberapa persyaratan atau hubungan tambahan yang dapat sangat memengaruhi model dan hubungan yang berfungsi dalam kasus yang lebih sederhana.

dietbuddha
sumber
Saya setuju tetapi ini hanyalah contoh kecil dan tidak mencoba menjadi model dunia. Misalnya, Anda dapat memiliki Shark yang memakan Ban dan Pelat Lisensi. Anda bisa membuat kelas abstrak induk dengan metode yang memakan segala jenis objek dan Makanan dapat memperpanjang kelas abstak ini.
hagensoft
@ Hortensoft: Setuju. Saya kadang-kadang terbawa suasana karena saya terus-menerus melihat pemodelan pengembang berdasarkan metafora yang langsung mereka gunakan, alih-alih melihat bagaimana data perlu dikonsumsi dan digunakan. Mereka menikah dengan desain OO berdasarkan ide awal dan kemudian mencoba dan memaksa masalah agar sesuai dengan solusi mereka alih-alih membuat solusi mereka sesuai dengan masalah.
dietbuddha
1

Pendekatan komposisi-kelebihan-warisan ECS:

An entity is a collection of components.
Systems process entities through their components.

Lion has claws and fangs as weapons.
Lion has meat as food.
Lion has a hunger for meat.
Lion has an affinity towards other lions.

Deer has antlers and hooves as weapons.
Deer has meat as food.
Deer has a hunger for plants.

Grass has plant as food.

Kodesemu:

lion = new Entity("Lion")
lion.put(new Claws)
lion.put(new Fangs)
lion.put(new Meat)
lion.put(new MeatHunger)
lion.put(new Affinity("Lion"))

deer = new Entity("Deer")
deer.put(new Antlers)
deer.put(new Hooves)
deer.put(new PlantHunger)

grass = new Entity("Grass")
grass.put(new Plant)

Natureadalah systemyang loop melalui entitas ini, mencari komponen apa yang mereka miliki melalui fungsi permintaan umum. Natureakan menyebabkan entitas dengan kelaparan daging menyerang entitas lain yang memiliki daging sebagai makanan menggunakan senjata mereka, kecuali mereka memiliki afinitas terhadap entitas itu. Jika serangan berhasil, entitas akan memakan korbannya, pada titik mana korban akan berubah menjadi mayat yang kekurangan daging. Natureakan menyebabkan entitas yang haus akan tanaman untuk memakan entitas yang memiliki tanaman sebagai makanan, asalkan mereka ada.

Nature({lion, deer, grass})

Nature(entities)
{
    for each entity in entities:
    {
       if entity.has("MeatHunger"):
           attack_meat(entity, entities.with("Meat", exclude = entity))
       if entity.has("PlantHunger"):
           eat_plants(entity, entites.with("Plant", exclude = entity))
    }
}

Mungkin kita ingin memperluas Grassuntuk membutuhkan sinar matahari dan air, dan kami ingin memperkenalkan sinar matahari dan air ke dunia kita. Namun Grasstidak dapat mencari ini secara langsung, karena tidak ada mobility. Animalsmungkin juga membutuhkan air, tetapi dapat secara aktif mencarinya karena mereka memiliki air mobility. Sangat mudah untuk terus memperluas dan mengubah model ini tanpa merusak seluruh desain, karena kami hanya menambahkan komponen baru dan memperluas perilaku sistem kami (atau jumlah sistem).


sumber
0

Tanpa menggunakan pewarisan berganda, dan menggunakan desain berorientasi objek, bagaimana Anda mengatasi masalah ini?

Seperti kebanyakan hal, itu tergantung .

Tergantung pada apa yang Anda lihat menjadi masalah ini.

  • Apakah ini masalah implementasi umum , misalnya bagaimana 'menyiasati' ketiadaan pewarisan berganda di platform yang Anda pilih?
  • Apakah ini masalah desain hanya untuk kasus khusus ini , misalnya bagaimana membuat model fakta bahwa hewan juga makanan?
  • Apakah ini merupakan masalah filosofis dengan model domain , misalnya apakah klasifikasi 'makanan' dan 'hewan' valid, perlu, dan cukup untuk aplikasi praktis yang dibayangkan?

Jika Anda bertanya tentang masalah implementasi umum, jawabannya akan tergantung pada kemampuan lingkungan Anda. Antarmuka IFood dan IAnimal dapat bekerja, dengan subclass EdibleAnimal yang mengimplementasikan kedua antarmuka. Jika lingkungan Anda tidak mendukung antarmuka, buat saja Hewan mewarisi dari Makanan.

Jika Anda bertanya tentang masalah desain khusus ini, cukup buat Hewan mewarisi dari Makanan. Itu hal paling sederhana yang mungkin bisa berhasil.

Jika Anda bertanya tentang konsep desain ini, jawabannya sangat tergantung pada apa yang ingin Anda lakukan dengan model tersebut. Jika itu untuk video game anjing-makan-anjing atau bahkan aplikasi untuk melacak jadwal makan di kebun binatang, mungkin cukup untuk bekerja. Jika itu untuk model konseptual untuk pola perilaku hewan, itu mungkin sedikit dangkal.

Steven A. Lowe
sumber
0

Warisan harus digunakan untuk sesuatu yang selalu merupakan hal lain, dan tidak dapat berubah. Rumput tidak selalu makanan. Misalnya, saya tidak makan rumput.

Rumput memainkan peran bahan makanan untuk hewan tertentu.

Neil McGuigan
sumber
Itu hanya sebuah abstraksi. Jika itu adalah persyaratan maka Anda bisa membuat lebih banyak divisi yang memperpanjang kelas abstrak Plant dan membuat manusia makan kelas abstrak seperti 'HumanEatablePlants' yang akan mengelompokkan tanaman yang dimakan manusia ke dalam kelas beton.
hagensoft
0

Anda baru saja menemukan batasan dasar OO.

OO bekerja dengan baik dengan struktur hierarkis. Tapi begitu Anda menjauh dari hierarki yang ketat abstraksi tidak bekerja dengan baik.

Saya tahu semua tentang komposisi metamorfosis dll. Yang digunakan untuk mengatasi keterbatasan ini tetapi mereka canggung, dan, yang lebih penting mengarah pada kode yang tidak jelas dan sulit untuk diikuti.

Basis data relasional diciptakan terutama untuk menjauh dari keterbatasan struktur hierarkis yang ketat.

Sebagai contoh, rumput juga bisa menjadi bahan bangunan, bahan baku kertas, bahan pakaian, rumput liar atau tanaman.

Rusa bisa menjadi hewan peliharaan, ternak, binatang kebun binatang, atau spesies yang dilindungi.

Singa juga bisa menjadi binatang kebun binatang atau spesies yang dilindungi.

Hidup itu tidak sederhana.

James Anderson
sumber
0

Tanpa menggunakan pewarisan berganda, dan menggunakan desain berorientasi objek, bagaimana Anda mengatasi masalah ini?

Apa masalahnya? Apa yang dilakukan sistem ini?Sampai Anda menjawab itu, saya tidak tahu kelas apa yang mungkin diperlukan. Apakah Anda mencoba membuat model ekologi, dengan karnivora dan herbivora dan tanaman, memproyeksikan populasi spesies ke masa depan? Apakah Anda mencoba agar komputer memainkan 20 pertanyaan?

Buang-buang waktu untuk memulai desain sebelum ada kasus penggunaan yang telah ditentukan. Saya telah melihat ini dibawa ke ekstrem konyol ketika tim sekitar sepuluh mulai memproduksi model OO dari sebuah Maskapai menggunakan perangkat lunak melalui gambar. Mereka bekerja selama dua tahun menjadi model tanpa memikirkan masalah bisnis yang sebenarnya. Akhirnya pelanggan bosan menunggu dan meminta tim untuk menyelesaikan masalah yang sebenarnya. Semua pemodelan itu sama sekali tidak berguna.

kevin cline
sumber