Saat ini saya sedang merancang ulang Sistem Entitas saya , untuk C ++, dan saya memiliki banyak Manajer. Dalam desain saya, saya memiliki kelas-kelas ini, untuk mengikat perpustakaan saya bersama. Saya telah mendengar banyak hal buruk ketika datang ke kelas "manajer", mungkin saya tidak menyebutkan kelas saya dengan tepat. Namun, saya tidak tahu apa lagi untuk memberi nama mereka.
Sebagian besar manajer, di perpustakaan saya, terdiri dari kelas-kelas ini (meskipun sedikit berbeda):
- Wadah - wadah untuk objek di manajer
- Atribut - atribut untuk objek di manajer
Dalam desain baru saya untuk perpustakaan saya, saya memiliki kelas-kelas khusus ini, untuk mengikat perpustakaan saya bersama-sama.
ComponentManager - mengelola komponen dalam Sistem Entitas
- ComponentContainer
- Komponen Komponen
- Adegan * - referensi ke Adegan (lihat di bawah)
SystemManager - mengelola sistem dalam Sistem Entitas
- SystemContainer
- Adegan * - referensi ke Adegan (lihat di bawah)
EntityManager - mengelola entitas dalam Sistem Entity
- EntityPool - kumpulan entitas
- EntityAttributes - atribut entitas (ini hanya akan dapat diakses oleh kelas ComponentContainer dan Sistem)
- Adegan * - referensi ke Adegan (lihat di bawah)
Adegan - mengikat semua manajer bersama
- Manajer Komponen
- Manajer sistem
- Manajer Entity
Saya berpikir untuk hanya menempatkan semua wadah / kolam di kelas Scene itu sendiri.
yaitu
Alih-alih ini:
Scene scene; // create a Scene
// NOTE:
// I technically could wrap this line in a createEntity() call in the Scene class
Entity entity = scene.getEntityManager().getPool().create();
Ini akan menjadi ini:
Scene scene; // create a Scene
Entity entity = scene.getEntityPool().create();
Tapi saya tidak yakin. Jika saya melakukan yang terakhir, itu berarti saya akan memiliki banyak objek dan metode yang dideklarasikan di dalam kelas Scene saya.
CATATAN:
- Sistem entitas hanyalah desain yang digunakan untuk game. Ini terdiri dari 3 bagian utama: komponen, entitas, dan sistem. Komponen hanyalah data, yang dapat "ditambahkan" ke entitas, agar entitas menjadi berbeda. Entitas diwakili oleh integer. Sistem mengandung logika untuk suatu entitas, dengan komponen tertentu.
- Alasan saya mengubah desain untuk perpustakaan saya, adalah karena saya pikir itu bisa diubah cukup banyak, saya tidak suka rasa / aliran ke sana, saat ini.
sumber
Jawaban:
DeadMG tepat pada spesifik kode Anda tetapi saya merasa seperti itu kehilangan klarifikasi. Juga saya tidak setuju dengan beberapa rekomendasinya yang tidak berlaku dalam konteks tertentu seperti kebanyakan pengembangan videogame berkinerja tinggi. Tapi dia secara global benar bahwa sebagian besar kode Anda tidak berguna saat ini.
Seperti yang dikatakan Dunk, kelas Manajer dipanggil seperti ini karena mereka "mengelola" berbagai hal. "kelola" seperti "data" atau "lakukan", ini adalah kata abstrak yang dapat berisi hampir semua hal.
Saya sebagian besar berada di tempat yang sama dengan Anda sekitar 7 tahun yang lalu, dan saya mulai berpikir bahwa ada sesuatu yang salah dalam cara berpikir saya karena begitu banyak upaya untuk membuat kode untuk tidak melakukan apa pun.
Apa yang saya ubah untuk memperbaikinya adalah mengubah kosakata yang saya gunakan dalam kode. Saya benar-benar menghindari kata-kata umum (kecuali kode generik, tetapi jarang ketika Anda tidak membuat perpustakaan umum). Saya menghindari menyebutkan nama "Manajer" atau "objek" apa pun.
Dampaknya langsung dalam kode: itu memaksa Anda untuk menemukan kata yang tepat sesuai dengan tanggung jawab nyata dari tipe Anda. Jika Anda merasa jenis melakukan beberapa hal (membuat indeks Buku, tetap hidup, membuat / menghancurkan buku) maka Anda memerlukan berbagai jenis yang masing-masing akan memiliki satu tanggung jawab, kemudian Anda menggabungkannya dalam kode yang menggunakannya. Kadang saya butuh pabrik, kadang tidak. Kadang saya membutuhkan registry, jadi saya setup satu (menggunakan kontainer standar dan smart pointer). Kadang-kadang saya memerlukan sistem yang terdiri dari beberapa sub sistem yang berbeda sehingga saya memisahkan semuanya sebanyak yang saya bisa sehingga setiap bagian melakukan sesuatu yang bermanfaat.
Jangan pernah menyebutkan tipe "manajer" dan pastikan semua tipe Anda memiliki peran unik adalah rekomendasi saya. Mungkin sulit untuk menemukan nama kapan-kapan, tetapi itu adalah salah satu hal tersulit untuk dilakukan dalam pemrograman secara umum
sumber
dynamic_cast
membunuh kinerja Anda, maka gunakan sistem tipe statis - itulah gunanya. Ini benar-benar konteks khusus, bukan yang umum, harus memutar RTTI Anda sendiri seperti yang dilakukan LLVM. Bahkan kemudian, mereka tidak melakukan ini.template <typename T> class Class { ... };
sebenarnya untuk menetapkan ID untuk kelas yang berbeda (yaitu komponen khusus dan sistem khusus). Penugasan ID digunakan untuk menyimpan komponen / sistem dalam vektor, karena peta mungkin tidak membawa kinerja yang saya butuhkan untuk sebuah game.Yah, saya telah membaca beberapa kode yang Anda tautkan, dan posting Anda, dan ringkasan jujur saya adalah bahwa sebagian besar pada dasarnya sama sekali tidak berharga. Maaf. Maksud saya, Anda memiliki semua kode ini, tetapi Anda belum mencapai apa pun. Sama sekali. Aku harus pergi ke kedalaman di sini, jadi bersabarlah denganku.
Mari kita mulai dengan ObjectFactory . Pertama, pointer fungsi. Ini adalah stateless, satu-satunya cara stateless yang berguna untuk membuat objek adalah
new
dan Anda tidak memerlukan pabrik untuk itu. Membuat objek secara stateful adalah hal yang berguna dan fungsi pointer tidak berguna untuk tugas ini. Dan kedua, setiap objek perlu tahu cara menghancurkan dirinya sendiri , tidak harus menemukan contoh yang tepat untuk menghancurkannya. Dan di samping itu, Anda harus mengembalikan pointer cerdas, bukan pointer mentah, untuk pengecualian dan keamanan sumber daya pengguna Anda. Seluruh kelas ClassRegistryData Anda adilstd::function<std::unique_ptr<Base, std::function<void(Base*)>>()>
, tetapi dengan lubang yang disebutkan di atas. Tidak perlu ada sama sekali.Kemudian kita memiliki AllocatorFunctor - sudah ada antarmuka pengalokasi standar yang disediakan - Belum lagi mereka hanya pembungkus
delete obj;
danreturn new T;
- yang lagi-lagi sudah disediakan, dan mereka tidak akan berinteraksi dengan pengalokasi memori stateful atau kustom yang ada.Dan ClassRegistry sederhana
std::map<std::string, std::function<std::unique_ptr<Base, std::function<void(Base*)>>()>>
tetapi Anda menulis kelas untuk itu alih-alih menggunakan fungsi yang ada. Itu sama, tetapi benar-benar tidak bisa diubah negara global dengan sekelompok makro jelek.Apa yang saya katakan di sini adalah bahwa Anda telah membuat beberapa kelas di mana Anda dapat mengganti semuanya dengan fungsionalitas yang dibangun dari kelas Standar, dan kemudian membuatnya lebih buruk dengan menjadikannya negara yang bisa berubah secara global dan melibatkan banyak makro, yang merupakan hal terburuk yang bisa Anda lakukan.
Kemudian kami memiliki Identifikasi . Ini banyak cerita yang sama di sini -
std::type_info
sudah melakukan tugas mengidentifikasi setiap jenis sebagai nilai run-time, dan itu tidak memerlukan boilerplate yang berlebihan pada bagian dari pengguna.Masalah terpecahkan, tetapi tanpa dua ratus baris makro sebelumnya dan yang lainnya. Ini juga menandai IdentifiableArray Anda sebagai apa pun kecuali
std::vector
Anda harus menggunakan penghitungan referensi meskipun kepemilikan unik atau non-kepemilikan akan lebih baik.Oh, dan apakah saya menyebutkan bahwa Jenis berisi sekelompok typedef yang sama sekali tidak berguna ? Anda benar-benar tidak mendapatkan keuntungan apa pun jika menggunakan jenis mentah secara langsung, dan kehilangan sedikit keterbacaan.
Selanjutnya adalah ComponentContainer . Anda tidak dapat memilih kepemilikan, Anda tidak dapat memiliki wadah terpisah untuk kelas turunan dari berbagai komponen, Anda tidak dapat menggunakan semantik seumur hidup Anda sendiri. Hapus tanpa penyesalan.
Sekarang, ComponentFilter mungkin benar-benar bernilai sedikit, jika Anda banyak refactored. Sesuatu seperti
Ini tidak berurusan dengan kelas yang berasal dari kelas yang Anda coba atasi, tetapi kelas Anda juga tidak.
Selebihnya dari kisah ini hampir sama. Tidak ada apa pun di sini yang tidak dapat dilakukan dengan lebih baik tanpa menggunakan perpustakaan Anda.
Bagaimana Anda menghindari manajer dalam kode ini? Hapus pada dasarnya semua itu.
sumber
typeid
ataudynamic_cast
. Terima kasih atas umpan baliknya, saya menghargainya.Masalah dengan kelas-kelas Manajer adalah bahwa seorang manajer dapat melakukan apa saja. Anda tidak dapat membaca nama kelas dan tahu apa yang dikerjakan kelas. EntityManager .... Saya tahu itu melakukan sesuatu dengan Entitas tetapi siapa yang tahu apa. SystemManager ... ya, itu mengelola sistem. Itu sangat membantu.
Dugaan saya adalah bahwa kelas manajer Anda mungkin melakukan banyak hal. Saya berani bertaruh jika Anda membagi hal-hal itu menjadi bagian-bagian fungsional yang terkait erat, maka Anda mungkin akan dapat menghasilkan nama-nama kelas yang terkait dengan bagian-bagian fungsional yang setiap orang dapat mengatakan apa yang mereka lakukan hanya dengan melihat nama kelas. Ini akan membuat pemeliharaan dan pemahaman desain menjadi jauh lebih mudah.
sumber
Saya sebenarnya tidak berpikir
Manager
sangat buruk dalam konteks ini, mengangkat bahu. Jika ada satu tempat di mana saya pikirManager
dimaafkan, itu akan untuk sistem entitas-komponen, karena itu benar-benar " mengelola " masa hidup komponen, entitas, dan sistem. Paling tidak itu adalah nama yang lebih bermakna di sini daripada, katakanlah,Factory
yang tidak masuk akal dalam konteks ini karena ECS benar-benar melakukan sedikit lebih banyak daripada menciptakan sesuatu.Saya kira Anda bisa menggunakan
Database
, sepertiComponentDatabase
. Atau Anda mungkin hanya menggunakanComponents
,,Systems
danEntities
(ini adalah apa yang saya gunakan secara pribadi dan terutama hanya karena saya suka pengidentifikasi singkat meskipun diakui menyampaikan informasi lebih sedikit, mungkin, daripada "Manajer").Satu bagian yang saya temukan agak kikuk adalah ini:
Itu banyak hal
get
sebelum Anda dapat membuat entitas dan rasanya desain bocor sedikit detail implementasi jika Anda harus tahu tentang kumpulan entitas dan "manajer" untuk membuatnya. Saya pikir hal-hal seperti "kumpulan" dan "manajer" (jika Anda berpegang pada nama ini) harus menjadi detail implementasi ECS, bukan sesuatu yang terpapar pada klien.Tapi tak tahu, saya telah melihat beberapa penggunaan yang sangat imajinatif dan generik
Manager
,Handler
,Adapter
, dan nama-nama semacam ini, tapi saya pikir ini adalah kasus penggunaan cukup dimaafkan ketika hal yang disebut "Manager" sebenarnya bertanggung jawab untuk "mengelola" ( "mengendalikan? "," handling? ") masa hidup objek, yang bertanggung jawab untuk membuat dan menghancurkan dan menyediakan akses kepada mereka secara individual. Setidaknya itu adalah penggunaan "Manajer" yang paling mudah dan intuitif bagi saya.Yang mengatakan, satu strategi umum untuk menghindari "Manajer" di nama Anda hanya mengambil bagian "Manajer" dan menambahkan
-s
di akhir. :-D Sebagai contoh, katakanlah Anda memilikiThreadManager
dan bertanggung jawab untuk membuat utas dan memberikan informasi tentang mereka dan mengaksesnya dan sebagainya, tetapi itu tidak mengumpulkan utas jadi kami tidak dapat menyebutnyaThreadPool
. Nah, kalau begitu, sebut saja begituThreads
. Lagi pula itulah yang saya lakukan ketika saya tidak imajinatif.Saya agak diingatkan kembali ketika padanan analog dari "Windows Explorer" disebut "File Manager", seperti:
Dan saya benar-benar berpikir "File Manager" dalam hal ini lebih deskriptif daripada "Windows Explorer" bahkan jika ada beberapa nama yang lebih baik di luar sana yang tidak melibatkan "Manager". Jadi setidaknya tolong jangan terlalu suka dengan nama Anda dan mulai memberi nama hal-hal seperti "ComponentExplorer". "ComponentManager" setidaknya memberi saya ide yang masuk akal tentang apa yang dilakukan orang itu yang bekerja dengan mesin ECS.
sumber
Manager
sesuai. Kelas mungkin memiliki masalah desain , tetapi nama tepat waktu