Bisakah Anda Miliki Abstrak / Kelas “Kosong”?


10

Tentu saja bisa, saya hanya ingin tahu apakah rasional untuk mendesain sedemikian rupa.

Saya membuat klon breakout dan sedang melakukan beberapa desain kelas. Saya ingin menggunakan warisan, meskipun saya tidak harus, untuk menerapkan apa yang telah saya pelajari di C ++. Saya sedang berpikir tentang desain kelas dan datang dengan sesuatu seperti ini:

GameObject -> kelas dasar (terdiri dari anggota data seperti offset x dan y, dan vektor SDL_Surface *

MovableObject: GameObject -> kelas abstrak + kelas turunan dari GameObject (satu metode batal bergerak () = 0;)

NonMovableObject: GameObject -> kelas kosong ... tidak ada metode atau anggota data selain konstruktor dan destruktor (setidaknya untuk saat ini?).

Kemudian saya berencana untuk mendapatkan kelas dari NonMovableObject, seperti Tileset: NonMovableObject. Saya hanya ingin tahu apakah kelas abstrak "kosong" atau hanya kelas kosong sering digunakan ... Saya perhatikan bahwa cara saya melakukan ini, saya hanya membuat kelas NonMovableObject hanya untuk kategorisasi.

Saya tahu saya terlalu memikirkan hal-hal hanya untuk membuat klon breakout, tetapi fokus saya kurang pada permainan dan lebih banyak menggunakan pewarisan dan merancang semacam kerangka permainan.

Jawaban:


2

Dalam C ++ Anda memiliki Multiple Inheritance sehingga benar-benar tidak ada manfaatnya memiliki kelas kosong tersebut. Jika Anda ingin Ball mewarisi dari GameObject dan MovableObject nanti (katakanlah, misalnya, Anda ingin memegang array GameObjects dan memanggil metode Centang setiap menit untuk membuat semuanya bergerak), itu cukup mudah untuk dilakukan.

Tetapi, dalam situasi itu, saya pribadi menyarankan agar Anda mengingat aksioma " lebih memilih komposisi (enkapsulasi) daripada warisan " dan melihat pola Negara . Jika Anda ingin setiap objek memiliki status gerakannya nanti, berikan ke GameObject. Misalnya: DiagonalBouncingMovementState untuk bola, HoriazontalControlledMovementState untuk dayung, NoMovementState untuk batu bata.

1) Ini akan memudahkan Anda untuk menulis unit test , jika Anda memilih untuk (yang, saya sarankan) - karena Anda dapat menguji GameObject dan masing-masing negara bagian Anda secara mandiri.

2) Katakanlah Anda memiliki token yang jatuh dari batu bata tetapi kemudian Anda ingin mengubah salah satu dari mereka sehingga mereka bergerak secara diagonal - alih-alih menggesernya di sekitar hierarki kompleks kelas warisan, Anda hanya dapat mengubah status gerakan hardcoded yang akan diteruskan ke sana .

3) Yang paling penting: Ketika Anda ingin mulai mengubah dalam game bagaimana bola dan / atau dayung bergerak berdasarkan token itu, Anda hanya terus mengubah status gerakannya (DiagonalMovementState menjadi DiagonalWithGravityMovementState).

Sekarang, semua ini tidak menunjukkan bahwa warisan selalu buruk, hanya untuk menunjukkan mengapa kami lebih memilih enkapsulasi daripada warisan. Anda mungkin masih ingin mendapatkan setiap jenis objek dari GameObject dan membuat kelas-kelas tersebut memahami Status Gerakan awal mereka. Anda hampir pasti ingin menurunkan setiap status gerakan Anda dari kelas MovementState yang abstrak karena C ++ tidak memiliki antarmuka.

$ 0,02


8

Di Jawa, "kosong" antarmuka digunakan sebagai penanda (misalnya Serializable), karena pada saat runtime, objek dapat diperiksa apakah mereka "mengimplementasikan" antarmuka itu; tetapi dalam C ++, sepertinya tidak ada gunanya bagi saya.

IMO, fitur bahasa harus dianggap alat, sesuatu yang membantu Anda mencapai tujuan tertentu, dan bukan kewajiban. Hanya karena Anda dapat menggunakan fitur bukan berarti Anda harus melakukannya . Warisan yang tidak berarti tidak membuat program lebih baik.


3

Anda tidak terlalu memikirkannya; Anda berpikir dan itu bagus.

Seperti yang sudah dinyatakan, jangan gunakan fitur bahasa hanya karena ada di sana. Pengorbanan fitur belajar bahasa adalah kunci untuk desain yang baik.

Seseorang seharusnya tidak menggunakan kelas dasar untuk kategorisasi. Itu bisa melibatkan tipe pemeriksaan. Itu ide yang buruk.

Pertimbangkan untuk menulis abstraksi murni yang mendefinisikan kontrak objek Anda. Kontrak objek Anda adalah antarmuka yang menghadap ke luar. Antarmuka publik. Apa yang dilakukannya.

Catatan: Penting untuk dipahami bahwa ini bukan data apa yang dimilikinya x, ytetapi apa yang dilakukannya move().

Dalam desain Anda, Anda memiliki kelas GameObject. Anda mengandalkan data dan bukan fungsionalitasnya. Ini terasa efisien dan benar untuk menggunakan warisan untuk berbagi apa yang tampaknya seperti data umum bersama, dalam kasus Anda xdan y.

Ini bukan penggunaan warisan yang benar. Selalu ingat, warisan adalah bentuk kopling paling ketat yang mungkin Anda miliki dan Anda harus berjuang untuk kopling longgar setiap saat.

Pada dasarnya sifatnya NonMovableObjecttidak bergerak. Ini xdan yharus dinyatakan const. Sesuai dengan sifatnya MoveableObjectperlu bergerak. Itu tidak bisa menyatakan xdan ysebagai const. Dengan demikian, ini bukan data yang sama dan tidak dapat dibagikan.

Pertimbangkan fungsionalitas objek Anda, atau kontrak. Apa yang harus mereka lakukan ? Lakukan dengan benar dan data akan datang. Kerjakan satu objek pada satu waktu. Khawatir tentang masing-masing pada gilirannya. Anda akan menemukan desain bagus Anda dapat digunakan kembali.

Mungkin tidak ada NonMovableObjectsama sekali. Bagaimana dengan abstraksi murni GameObjectBaseyang mendefinisikan move()metode? Sistem Anda menginstal GameObjectyang mengimplementasikan move()dan sistem hanya memindahkan yang perlu dipindahkan.

Ini baru permulaan; sebuah rasa. Lubang kelinci masuk lebih dalam. Tidak ada sendok.


Meskipun benar bahwa keduanya MovableObjecttidak NonMovableObjectdapat mewarisi satu sama lain, mereka berdua memiliki lokasi; karena itu, keduanya harus dikonsumsi oleh kode yang misalnya ingin mengarahkan tujuan monster ke suatu objek tanpa memperhatikan apakah objek itu mungkin bergerak atau tidak.
supercat

2

Ini memiliki aplikasi dalam C # seperti ammoQ yang disebutkan, tetapi dalam C + + satu-satunya aplikasi akan jika Anda ingin memiliki daftar pointer NonMovableObject.

Tapi kemudian saya membayangkan Anda akan merusak objek melalui pointer NonMoveableObject, dalam hal ini Anda akan membutuhkan destruktor virtual, dan itu tidak akan menjadi kelas kosong lagi. :)


Saya memikirkan kasus itu juga, tetapi apa yang dapat Anda lakukan dengan daftar objek yang tidak memperlihatkan perilaku? Kemungkinan besar, Anda entah bagaimana akan menemukan tipe asli dan menggunakan gips untuk mengakses metode dan bidang yang tersedia - pasti bau kode.
user281377

Ya itu poin yang bagus.
tenpn

1

Satu situasi di mana Anda mungkin melihat ini adalah di mana Anda ingin koleksi yang sangat tyuped mengandung tipe heterogen. Saya tidak mengatakan ini ide yang bagus, ini mungkin menunjukkan abstraksi yang lemah atau desain yang buruk, tetapi itu terjadi. Seperti yang Anda sebutkan itu adalah cara mengkategorikan hal-hal dan dapat bermanfaat dan sangat valid.

Misalnya Anda ingin koleksi untuk menampung Kucing dan Anjing. Dalam desain Anda, Anda memutuskan mereka memiliki banyak kesamaan sehingga memiliki kelas dasar Mamal dan menggunakan jenis ini dalam koleksi Anda (misalnya Daftar). Semuanya bagus. Maka Anda memutuskan untuk menambahkan beberapa Frogs ke koleksi Anda, tetapi mereka bukan mamalia, jadi salah satu cara melakukan ini adalah dengan membuat Frogs dan Mamals menjadi subclass of Animal, tetapi mungkin Anda tidak bisa memikirkan banyak tentang Frogs dan Mamals di umum sehingga Hewan tetap kosong. Anda kemudian mengetik koleksi Anda dengan Animal bukan Mamal dan semuanya baik-baik saja.

Dalam kehidupan nyata saya menemukan bahwa meskipun saya membuat kelas dasar kosong cepat atau lambat beberapa fungsi berakhir di sana, tetapi pasti ada saat-saat ketika mereka ada.


Namun, satu pertanyaan yang harus diajukan adalah: jika jenisnya tidak memiliki kesamaan, mengapa mereka disimpan dalam koleksi yang sama? Tidak ada operasi yang dapat Anda lakukan pada semua item dalam koleksi, yang jenisnya mengalahkan tujuan memiliki koleksi seperti itu. Sekarang mungkin ada beberapa operasi buatan Anda mungkin ingin menambahkan kedua untuk menyederhanakan logika - misalnya menerapkan pola desain Pengunjung - di mana titik itu mungkin menjadi berguna lagi, tapi sekarang mereka memiliki sesuatu yang sama ...
Jules
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.