Apakah desain yang buruk memiliki 2 kelas yang saling membutuhkan?
Saya menulis permainan kecil di mana saya memiliki GameEngine
kelas yang memiliki beberapa GameState
objek. Untuk mengakses beberapa metode rendering, GameState
objek - objek ini juga perlu mengetahui GameEngine
kelasnya - 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.
c++
architecture
shad0w
sumber
sumber
Jawaban:
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:
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.
sumber
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.
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.
sumber
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 1
Update()
fungsi, dan Model memegang referensi ke objek yang dapat diobservasi, yang mungkin merupakan tampilan. Ketika Model berubah, ia memanggilUpdate()
semua Observable-nya, dan Lihat mengimplementasikanUpdate()
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:
sumber
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.
sumber