Kekhawatirannya adalah bahwa sejumlah besar kelas akan diterjemahkan menjadi mimpi buruk pemeliharaan. Pandangan saya adalah bahwa itu akan memiliki efek sebaliknya.
Saya benar-benar di pihak teman Anda, tetapi bisa jadi masalah domain kami dan jenis masalah dan desain yang kami tangani dan terutama jenis hal apa yang mungkin membutuhkan perubahan di masa depan. Masalah berbeda, solusi berbeda. Saya tidak percaya benar atau salah, hanya programmer yang berusaha menemukan cara terbaik yang mereka bisa untuk memecahkan masalah desain khusus mereka. Saya bekerja di VFX yang tidak terlalu berbeda dengan mesin game.
Tetapi masalah bagi saya bahwa saya berjuang dengan apa yang paling tidak bisa disebut sedikit lebih dari arsitektur SOLID-conforming (itu berbasis COM), mungkin secara kasar dididihkan ke "terlalu banyak kelas" atau "terlalu banyak fungsi" sebagai teman Anda mungkin menjelaskan. Saya akan secara khusus mengatakan, "terlalu banyak interaksi, terlalu banyak tempat yang mungkin dapat berperilaku tidak pantas, terlalu banyak tempat yang dapat menyebabkan efek samping, terlalu banyak tempat yang mungkin perlu diubah, dan terlalu banyak tempat yang mungkin tidak melakukan apa yang kami pikir mereka lakukan . "
Kami memiliki beberapa antarmuka abstrak (dan murni) yang diimplementasikan oleh sejumlah subtipe, seperti itu (membuat diagram ini dalam konteks berbicara tentang manfaat ECS, mengabaikan komentar kiri bawah):
Di mana antarmuka gerak atau antarmuka node adegan dapat diimplementasikan oleh ratusan subtipe: lampu, kamera, jerat, pemecah fisika, shader, tekstur, tulang, bentuk primitif, kurva, dll. (Dan sering ada beberapa jenis masing-masing ). Dan masalah utamanya adalah desain itu tidak begitu stabil. Kami memiliki persyaratan yang berubah dan terkadang antarmuka sendiri harus berubah, dan ketika Anda ingin mengubah antarmuka abstrak yang diterapkan oleh 200 subtipe, itu adalah perubahan yang sangat mahal. Kami mulai mengurangi hal itu dengan menggunakan kelas dasar abstrak di antaranya mengurangi biaya perubahan desain seperti itu, tetapi mereka masih mahal.
Jadi sebagai alternatif saya mulai menjelajahi arsitektur sistem entitas-komponen yang digunakan agak umum di industri game. Itu mengubah segalanya menjadi seperti ini:
Dan wow! Itu adalah perbedaan dalam hal rawatan. Ketergantungan tidak lagi mengalir menuju abstraksi , tetapi menuju data (komponen). Dan setidaknya dalam kasus saya, data jauh lebih stabil dan lebih mudah untuk mendapatkan yang benar dalam hal desain dimuka terlepas dari perubahan persyaratan (meskipun apa yang bisa kita lakukan dengan data yang sama terus berubah dengan perubahan persyaratan).
Juga karena entitas dalam ECS menggunakan komposisi alih-alih pewarisan, mereka sebenarnya tidak perlu mengandung fungsionalitas. Mereka hanyalah "wadah komponen" analog. Itu membuatnya begitu 200 subtipe analog yang mengimplementasikan antarmuka gerak berubah menjadi 200 entitas contoh (bukan tipe terpisah dengan kode terpisah) yang hanya menyimpan komponen gerakan (yang tidak lain adalah data yang terkait dengan gerakan). A PointLight
tidak lagi menjadi kelas / subtipe yang terpisah. Ini bukan kelas sama sekali. Ini adalah instance dari entitas yang hanya menggabungkan beberapa komponen (data) yang terkait dengan keberadaannya di ruang (gerak) dan sifat spesifik lampu titik. Satu-satunya fungsi yang terkait dengannya adalah di dalam sistem, sepertiRenderSystem
, yang mencari komponen cahaya dalam adegan untuk menentukan cara membuat adegan.
Dengan perubahan persyaratan di bawah pendekatan ECS, sering kali hanya ada kebutuhan untuk mengubah satu atau dua sistem yang beroperasi pada data itu atau hanya memperkenalkan sistem baru di samping, atau memperkenalkan komponen baru jika data baru diperlukan.
Jadi untuk domain saya setidaknya, dan saya hampir yakin itu bukan untuk semua orang, ini membuat segalanya jauh lebih mudah karena ketergantungan mengalir menuju stabilitas (hal-hal yang tidak perlu sering berubah sama sekali). Itu tidak terjadi dalam arsitektur COM ketika dependensi secara seragam mengalir menuju abstraksi. Dalam kasus saya, jauh lebih mudah untuk mencari tahu data apa yang diperlukan untuk gerak di muka daripada semua hal yang mungkin Anda lakukan dengannya, yang sering berubah sedikit selama berbulan-bulan atau bertahun-tahun ketika persyaratan baru datang.
Apakah ada kasus di OOP di mana beberapa atau semua prinsip SOLID tidak meminjamkan diri untuk membersihkan kode?
Yah, kode bersih saya tidak bisa mengatakan karena beberapa orang menyamakan kode bersih dengan SOLID, tapi pasti ada beberapa kasus di mana memisahkan data dari fungsi seperti ECS, dan mengarahkan kembali ketergantungan menjauh dari abstraksi terhadap data pasti dapat membuat banyak hal lebih mudah untuk dilakukan. berubah, untuk alasan penggandengan yang jelas, jika data akan jauh lebih stabil daripada abstraksi. Tentu saja ketergantungan pada data dapat membuat pemeliharaan invarian menjadi sulit, tetapi ECS cenderung memitigasi hal tersebut seminimal mungkin dengan sistem organisasi yang meminimalkan jumlah sistem yang mengakses setiap jenis komponen tertentu.
Itu tidak selalu bahwa dependensi harus mengalir ke abstraksi seperti DIP akan menyarankan; ketergantungan harus mengalir ke hal-hal yang sangat tidak mungkin membutuhkan perubahan di masa depan. Itu mungkin atau mungkin bukan abstraksi dalam semua kasus (jelas bukan milik saya).
- Ya, ada prinsip-prinsip desain OOP yang sebagian bertentangan dengan SOLID
- Ya, ada prinsip-prinsip desain OOP yang sepenuhnya bertentangan dengan SOLID.
Saya tidak yakin apakah ECS benar-benar citarasa OOP. Beberapa orang mendefinisikannya seperti itu, tetapi saya melihatnya sangat berbeda secara inheren dengan karakteristik kopling dan pemisahan data (komponen) dari fungsionalitas (sistem) dan kurangnya enkapsulasi data. Jika itu dianggap sebagai bentuk OOP, saya akan berpikir itu sangat bertentangan dengan SOLID (setidaknya ide-ide ketat SRP, buka / tutup, substitusi liskov, dan DIP). Tapi saya harap ini adalah contoh yang masuk akal dari satu kasus dan domain di mana aspek paling mendasar dari SOLID, setidaknya karena orang umumnya akan menafsirkannya dalam konteks OOP yang lebih dikenal, mungkin tidak begitu berlaku.
Kelas kecil
Saya sedang menjelaskan arsitektur dari salah satu game saya yang, yang mengejutkan teman saya, berisi banyak kelas kecil dan beberapa lapisan abstraksi. Saya berpendapat bahwa ini adalah hasil dari saya yang berfokus untuk memberikan segalanya Tanggung Jawab Tunggal dan juga melonggarkan hubungan antar komponen.
ECS telah banyak menantang dan mengubah pandangan saya. Seperti Anda, saya dulu berpikir bahwa gagasan tentang pemeliharaan adalah memiliki implementasi paling sederhana untuk hal-hal yang mungkin, yang menyiratkan banyak hal, dan lebih jauh lagi, banyak hal yang saling tergantung (bahkan jika saling ketergantungan adalah di antara abstraksi). Sangat masuk akal jika Anda memperbesar hanya pada satu kelas atau fungsi untuk ingin melihat implementasi yang paling mudah, sederhana, dan jika kita tidak melihatnya, refactor dan bahkan mungkin membusuk lebih lanjut. Tetapi akibatnya mudah untuk melewatkan apa yang terjadi dengan dunia luar, karena setiap kali Anda membagi sesuatu yang relatif kompleks menjadi 2 hal atau lebih, 2 hal atau lebih itu pasti berinteraksi * (lihat di bawah) satu sama lain di beberapa cara, atau sesuatu di luar harus berinteraksi dengan mereka semua.
Saat ini saya menemukan ada tindakan penyeimbangan antara kesederhanaan sesuatu dan berapa banyak hal yang ada dan berapa banyak interaksi yang diperlukan. Sistem dalam ECS cenderung lumayan dengan implementasi non-sepele untuk beroperasi pada data, seperti PhysicsSystem
atau RenderSystem
atau GuiLayoutSystem
. Namun, fakta bahwa produk yang kompleks membutuhkan begitu sedikit dari mereka cenderung membuatnya mudah untuk melangkah mundur dan alasan tentang perilaku keseluruhan basis kode seluruh. Ada sesuatu di sana yang mungkin menyarankan bahwa mungkin bukan ide yang buruk untuk bersandar pada sisi yang lebih sedikit, kelas bulkier (masih melakukan tanggung jawab tunggal yang bisa dibilang), jika itu berarti lebih sedikit kelas untuk dipertahankan dan dipikirkan, dan lebih sedikit interaksi di seluruh sistem.
Interaksi
Saya mengatakan "interaksi" daripada "penggandengan" (meskipun mengurangi interaksi berarti mengurangi keduanya), karena Anda dapat menggunakan abstraksi untuk memisahkan dua objek konkret, tetapi keduanya masih saling berbicara. Mereka masih bisa menimbulkan efek samping dalam proses komunikasi tidak langsung ini. Dan seringkali saya menemukan kemampuan saya untuk beralasan tentang kebenaran sistem terkait dengan "interaksi" ini lebih dari "penggandengan". Meminimalkan interaksi cenderung membuat banyak hal lebih mudah bagi saya untuk mempertimbangkan segala sesuatu dari pandangan mata burung. Itu berarti hal-hal yang tidak saling berbicara sama sekali, dan dari pengertian itu, ECS juga cenderung untuk benar-benar meminimalkan "interaksi", dan bukan hanya penggabungan, ke tingkat minimum minimum (setidaknya saya berlindung '
Yang mengatakan, ini mungkin setidaknya sebagian dan kelemahan pribadi saya. Saya telah menemukan hambatan terbesar bagi saya untuk menciptakan sistem skala besar, dan masih yakin alasan tentang mereka, menavigasi melalui mereka, dan merasa seperti saya dapat membuat perubahan potensial yang diinginkan di mana saja dengan cara yang dapat diprediksi, adalah manajemen negara dan sumber daya bersama dengan efek samping. Ini adalah kendala terbesar yang mulai muncul ketika saya beralih dari puluhan ribu LOC menjadi ratusan ribu LOC menjadi jutaan LOC, bahkan untuk kode yang saya tulis sendiri sepenuhnya. Jika ada sesuatu yang memperlambat saya untuk merangkak di atas segalanya, ini pengertian bahwa saya tidak bisa lagi memahami apa yang terjadi dalam hal status aplikasi, data, efek samping. Saya t' Bukan waktu robot yang diperlukan untuk membuat perubahan yang memperlambat saya, sama halnya dengan ketidakmampuan untuk memahami dampak penuh dari perubahan jika sistem tumbuh di luar kemampuan pikiran saya untuk memikirkannya. Dan mengurangi interaksi telah, bagi saya, cara paling efektif untuk memungkinkan produk tumbuh lebih besar dengan lebih banyak fitur tanpa saya secara pribadi kewalahan oleh hal-hal ini, karena mengurangi interaksi seminimal mungkin juga mengurangi jumlah tempat yang dapat bahkan mungkin mengubah status aplikasi dan menyebabkan efek samping secara substansial.
Itu dapat mengubah sesuatu seperti ini (di mana segala sesuatu dalam diagram memiliki fungsionalitas, dan jelas skenario dunia nyata akan memiliki banyak, berkali-kali jumlah objek, dan ini adalah diagram "interaksi", bukan kopling, sebagai kopling seseorang akan memiliki abstraksi di antaranya):
... untuk ini di mana hanya sistem yang memiliki fungsionalitas (komponen biru sekarang hanya data, dan sekarang ini adalah diagram kopling):
Dan ada pemikiran yang muncul tentang semua ini dan mungkin cara untuk membingkai beberapa manfaat ini dalam konteks OOP yang lebih sesuai yang lebih kompatibel dengan SOLID, tapi saya belum menemukan desain dan kata-kata dulu, dan saya menemukannya sulit karena terminologi saya terbiasa membuang semua yang berhubungan langsung dengan OOP. Saya terus berusaha mencari tahu membaca jawaban orang-orang di sini dan juga mencoba yang terbaik untuk merumuskan jawaban saya, tetapi ada beberapa hal yang sangat menarik tentang sifat ECS yang saya belum dapat dengan sempurna menempatkan jari saya pada itu mungkin dapat diterapkan secara lebih luas bahkan untuk arsitektur yang tidak menggunakannya. Saya juga berharap jawaban ini tidak muncul sebagai promosi ECS! Saya hanya merasa sangat menarik karena merancang ECS telah benar-benar mengubah pikiran saya secara drastis,