Ketergantungan kelas melingkar


12

Apakah desain yang buruk memiliki 2 kelas yang saling membutuhkan?

Saya menulis permainan kecil di mana saya memiliki GameEnginekelas yang memiliki beberapa GameStateobjek. Untuk mengakses beberapa metode rendering, GameStateobjek - objek ini juga perlu mengetahui GameEnginekelasnya - jadi ini adalah ketergantungan melingkar.

Apakah Anda menyebut desain yang buruk ini? Saya hanya bertanya, karena saya tidak begitu yakin dan saat ini saya masih bisa memperbaiki hal-hal ini.


2
Saya akan sangat terkejut jika jawabannya adalah ya.
doppelgreener

Karena ada dua pertanyaan, yang secara logis terpisah ... jawabannya adalah ya untuk salah satu dari mereka. Mengapa Anda ingin mendesain keadaan yang benar-benar melakukan sesuatu? Anda harus memiliki manajer / pengontrol untuk memeriksa keadaan dan melakukan tindakan .. Agak membingungkan untuk membiarkan jenis objek keadaan mengambil alih tanggung jawab orang lain. Jika Anda ingin melakukannya, C ++ adalah teman Anda .
teodron

Kelas GameState saya adalah hal-hal seperti intro, game aktual, menu, dan sebagainya. GameEngine menyimpan status ini dalam tumpukan, jadi saya dapat menjeda status dan membuka menu, atau memainkan cutscene, ... Kelas GameState perlu mengetahui GameEngine, karena mesin menciptakan jendela dan memiliki konteks render .
shad0w

5
Ingat, semua aturan ini bertujuan untuk cepat. Cepat dijalankan, Cepat dibuat, Cepat dipertahankan. Terkadang aspek-aspek ini bertentangan satu sama lain dan Anda perlu melakukan analisis biaya-manfaat untuk memutuskan bagaimana melanjutkan. Jika Anda terlalu memikirkannya, dan membuat panggilan, Anda masih melakukan lebih baik dari 90% pengembang. Tidak ada monster tersembunyi yang akan membunuhmu jika kau salah melakukannya, dia lebih seperti operator pintu tol.
DampeS8N

Jawaban:


0

Ini bukan desain yang buruk, tetapi dapat dengan mudah lepas kendali. Bahkan Anda menggunakan desain itu dalam kode sehari-hari Anda. Misalnya vektor tahu itu iterator pertama dan iterator memiliki pointer ke wadahnya.

Sekarang dalam kasus Anda, jauh lebih baik untuk memiliki dua kelas terpisah untuk GameEnigne dan GameState. Karena pada dasarnya keduanya melakukan hal-hal yang berbeda, dan kemudian Anda mungkin mendefinisikan banyak kelas yang mewarisi GameState (seperti GameState untuk setiap adegan dalam game Anda). Dan tidak ada yang bisa menyangkal kebutuhan mereka untuk memiliki akses satu sama lain. Pada dasarnya GameEngine menjalankan gamestate, jadi harus memiliki pointer ke mereka. Dan GameState menggunakan sumber daya yang ditentukan dalam GameEngine (seperti yang diberikan, manajer fisika, dll.).

Anda tidak dapat dan tidak boleh menggabungkan kedua kelas menjadi satu sama lain karena mereka melakukan hal yang berbeda secara alami dan menggabungkan akan menghasilkan kelas yang sangat besar yang tidak ada yang suka.

Sejauh ini kita tahu bahwa kita membutuhkan ketergantungan melingkar dalam desain keluar. Ada beberapa cara untuk membuatnya dengan aman:

  1. Seperti yang Anda sarankan, kita dapat menempatkan pointer masing-masing ke yang lain, itu adalah solusi paling sederhana tetapi mungkin dengan mudah lepas kendali.
  2. Anda juga dapat menggunakan desain singleton / multiton, dengan metode ini Anda harus mendefinisikan kelas GameEngine Anda sebagai singleton dan masing-masing GameStates tersebut sebagai multiton. Meskipun banyak pengembang menganggap singleton dan multiton sebagai AntiPattern, saya lebih suka desain ini.
  3. Anda dapat menggunakan variabel global, hei pada dasarnya hal yang sama seperti Singleton / multiton tetapi mereka sedikit perbedaan dalam hal bahwa mereka tidak membatasi programmer tidak untuk membuat instance sesuka hati.

Untuk menyimpulkan jawabannya, Anda mungkin menggunakan salah satu dari tiga metode tersebut tetapi Anda harus berhati-hati untuk tidak menggunakan salah satu dari mereka karena seperti yang saya katakan semua desain itu benar-benar berbahaya dan mungkin dengan mudah menghasilkan kode yang tidak dapat dipertahankan.


6

Apakah desain yang buruk memiliki 2 kelas yang saling membutuhkan?

Ini sedikit bau kode , tetapi orang bisa pergi begitu saja. Jika itu cara yang lebih mudah dan lebih cepat untuk menjalankan dan menjalankan game Anda, lakukanlah. Tetapi ingatlah itu karena ada peluang bagus Anda harus merevisinya di beberapa titik.

Masalahnya dengan C ++ adalah bahwa dependensi melingkar tidak dapat dikompilasi dengan mudah , jadi mungkin ide yang lebih baik untuk menyingkirkannya daripada menghabiskan waktu memperbaiki kompilasi Anda.

Lihat pertanyaan ini di SO untuk beberapa pendapat lagi.

Apakah Anda menyebut desain buruk [desain saya]?

Tidak, ini masih lebih baik daripada menempatkan semuanya dalam satu kelas.

Ini tidak terlalu bagus, tetapi sebenarnya cukup dekat dengan sebagian besar implementasi yang saya lihat. Biasanya, Anda akan memiliki kelas manajer untuk status permainan ( waspadalah! ), Dan kelas penyaji, dan sangat umum bahwa mereka adalah lajang. Jadi ketergantungan sirkular "tersembunyi", tetapi berpotensi ada di sana.

Juga, seperti yang Anda diberitahu dalam komentar, agak aneh bahwa kelas state game melakukan semacam rendering. Mereka seharusnya hanya menyimpan informasi keadaan, dan rendering harus ditangani oleh renderer, atau beberapa komponen grafis dari objek game itu sendiri.

Sekarang mungkin ada desain pamungkas . Saya ingin tahu apakah jawaban lain membawa satu ide bagus. Meski begitu, Anda mungkin adalah orang yang dapat menemukan desain terbaik untuk gim Anda.


Mengapa itu bau kode? Sebagian besar objek dengan hubungan orangtua-anak perlu saling melihat.
Kikaimaru

4
Karena itu cara termudah untuk tidak menentukan kelas mana yang bertanggung jawab untuk apa. Itu dengan mudah berakhir di dua kelas yang sangat dalam digabungkan sehingga menambahkan kode baru dapat dilakukan di salah satu dari mereka, sehingga mereka tidak lagi terpisah secara konsep. Ini juga merupakan pintu terbuka untuk kode spaghetti: kelas A memanggil kelas B untuk ini, tetapi B membutuhkan info dari A, dll. Jadi tidak, saya tidak akan mengatakan bahwa objek anak harus tahu tentang induknya. Jika memungkinkan, lebih baik jika tidak.
Laurent Couvidou

Itu kesalahan kelalaian yang licin. Kelas statis dapat menyebabkan hal-hal buruk juga, tetapi kelas util tidak berbau kode. Dan saya benar-benar ragu bahwa ada beberapa cara "bagus" untuk membuat sesuatu seperti Scene memiliki SceneNodes dan SceneNode memiliki referensi ke Scene, jadi Anda tidak dapat menambahkannya ke dua adegan yang berbeda ... Dan di mana Anda akan berhenti? Apakah A diperlukan B, B membutuhkan C dan C memerlukan A kode bau?
Kikaimaru

Memang, ini seperti kelas statis. Tangani dengan hati-hati, gunakan hanya jika Anda harus. Sekarang saya berbicara dari pengalaman dan saya bukan satu-satunya yang berpikir seperti ini , tapi, sungguh, ini masalah pendapat. Pendapat saya adalah bahwa dalam konteks yang diberikan oleh OP, ini memang bau.
Laurent Couvidou

Tidak disebutkan kasus itu di artikel wikipedia itu. Dan Anda tidak bisa mengatakan ini kasus "Keintiman yang tidak pantas" sampai Anda benar-benar melihat kelas-kelas itu.
Kikaimaru

6

Sering dianggap desain yang buruk harus memiliki 2 kelas yang merujuk langsung satu sama lain, ya. Secara praktis bisa lebih sulit untuk mengikuti aliran kontrol melalui kode, kepemilikan objek dan masa hidup mereka bisa rumit, itu berarti bahwa kelas tidak dapat digunakan kembali tanpa yang lain, itu bisa berarti bahwa aliran kontrol harus benar-benar hidup di luar kedua dari kelas-kelas ini di kelas 'mediator' ketiga, dan seterusnya.

Namun, sangat umum bagi 2 objek untuk saling merujuk, dan perbedaannya di sini adalah bahwa biasanya hubungan satu arah lebih abstrak. Misalnya, dalam sistem Model-View-Controller, objek View dapat menyimpan referensi ke objek Model, dan akan tahu semua tentangnya, bisa mengakses semua metode dan properti sehingga View dapat mengisi sendiri dengan data yang relevan . Objek Model dapat menyimpan referensi kembali ke Lihat sehingga dapat membuat pembaruan Lihat ketika datanya sendiri telah berubah. Tetapi alih-alih Model yang memiliki referensi Tampilan - yang akan membuat Model bergantung pada Tampilan - biasanya View mengimplementasikan antarmuka yang dapat Diobservasi, seringkali hanya dengan 1Update()fungsi, dan Model memegang referensi ke objek yang dapat diobservasi, yang mungkin merupakan tampilan. Ketika Model berubah, ia memanggil Update()semua Observable-nya, dan Lihat mengimplementasikan Update()dengan memanggil kembali ke Model dan mengambil informasi yang diperbarui. Manfaatnya di sini adalah bahwa Model tidak tahu apa-apa tentang Tampilan sama sekali (dan mengapa harus itu?), Dan dapat digunakan kembali dalam situasi lain, bahkan yang tanpa Tampilan.

Anda memiliki situasi yang serupa di gim Anda. GameEngine biasanya akan tahu tentang GameStates. Tetapi GameState tidak perlu tahu semua tentang GameEngine - hanya perlu akses ke metode rendering tertentu di GameEngine. Itu akan memicu sedikit alarm di kepala Anda yang mengatakan bahwa salah satu (a) GameEngine mencoba melakukan terlalu banyak hal dalam satu kelas, dan / atau (b) GameState tidak memerlukan seluruh mesin game, hanya bagian yang dapat diulang.

Tiga pendekatan untuk menyelesaikan ini termasuk:

  • Jadikan GameEngine berasal dari antarmuka Renderable, dan berikan referensi itu ke dalam GameState. Jika Anda pernah menolak bagian rendering, Anda hanya perlu memastikan itu membuat antarmuka yang sama.
  • Buat faktor bagian rendering menjadi objek Renderer baru, dan berikan itu ke GameStates sebagai gantinya.
  • Biarkan apa adanya. Mungkin pada titik tertentu Anda ingin mengakses semua fungsi GameEngine dari GameState. Tetapi perlu diingat bahwa perangkat lunak yang mudah dipelihara dan mudah diperluas biasanya mensyaratkan bahwa setiap kelas mengacu pada sesedikit mungkin di luar, yang mengapa memecah hal-hal menjadi sub-objek dan antarmuka yang melakukan fungsi tunggal dan baik. tugas yang ditentukan lebih disukai.

0

Secara umum dianggap praktik yang baik untuk memiliki kohesi tinggi dengan kopling rendah. Berikut adalah beberapa tautan tentang kopling dan kohesi.

kopling wikipedia

kohesi wikipedia

kopling rendah, kohesi tinggi

stackoverflow pada praktik terbaik

Memiliki dua kelas referensi satu sama lain adalah memiliki kopling tinggi. The google kerangka guice tujuan untuk mencapai kohesi tinggi dengan kopling rendah melalui sarana ketergantungan injeksi. Saya sarankan Anda membaca topik sedikit lebih banyak dan kemudian membuat panggilan Anda sendiri sesuai konteks Anda.

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.