Apakah ada rasa OOP di mana beberapa atau semua prinsip SOLID adalah antitesis terhadap kode bersih?


26

Baru-baru ini saya berdiskusi dengan seorang teman saya tentang OOP dalam pengembangan video game.

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.

Kekhawatirannya adalah bahwa sejumlah besar kelas akan diterjemahkan menjadi mimpi buruk pemeliharaan. Pandangan saya adalah bahwa itu akan memiliki efek sebaliknya. Kami melanjutkan untuk membahas ini selama berabad-abad, akhirnya setuju untuk tidak setuju, mengatakan bahwa mungkin ada kasus di mana prinsip-prinsip SOLID dan OOP yang tepat tidak benar-benar tercampur dengan baik.

Bahkan entri Wikipedia tentang prinsip-prinsip SOLID menyatakan bahwa itu adalah pedoman yang membantu untuk menulis kode yang dapat dipelihara dan bahwa mereka adalah bagian dari strategi keseluruhan pemrograman yang gesit dan adaptif.

Jadi, pertanyaan saya adalah:

Apakah ada kasus di OOP di mana beberapa atau semua prinsip SOLID tidak meminjamkan diri untuk membersihkan kode?

Saya dapat langsung membayangkan bahwa Prinsip Pergantian Liskov mungkin dapat bertentangan dengan rasa pewarisan aman lainnya. Dengan kata lain, jika seseorang menemukan pola bermanfaat lain yang diterapkan melalui pewarisan, sangat mungkin LSP mungkin bertentangan langsung dengannya.

Apakah ada yang lain? Mungkin jenis proyek tertentu atau platform target tertentu bekerja lebih baik dengan pendekatan yang kurang SOLID?

Edit:

Saya hanya ingin menentukan bahwa saya tidak bertanya bagaimana meningkatkan kode saya;) Satu-satunya alasan saya menyebutkan proyek dalam pertanyaan ini adalah untuk memberikan sedikit konteks. Pertanyaan saya adalah tentang OOP dan prinsip-prinsip desain secara umum .

Jika Anda ingin tahu tentang proyek saya, lihat ini .

Edit 2:

Saya membayangkan pertanyaan ini akan dijawab dalam satu dari 3 cara:

  1. Ya, ada prinsip-prinsip desain OOP yang sebagian bertentangan dengan SOLID
  2. Ya, ada prinsip-prinsip desain OOP yang sepenuhnya bertentangan dengan SOLID
  3. Tidak, SOLID adalah lutut lebah dan OOP selamanya akan lebih baik dengan itu. Tapi, seperti semua hal lainnya, ini bukan obat mujarab. Minum dengan bertanggung jawab.

Opsi 1 dan 2 kemungkinan akan menghasilkan jawaban yang panjang dan menarik. Opsi 3, di sisi lain, akan menjadi jawaban yang pendek, tidak menarik, tetapi meyakinkan secara keseluruhan.

Kami tampaknya akan menyatu ke opsi 3.


Bagaimana Anda mendefinisikan 'kode bersih'?
GrandmasterB

Kode bersih, dalam lingkup pertanyaan ini, adalah struktur kode dengan cara yang memenuhi tujuan OOP dan seperangkat prinsip desain yang dipilih. Jadi, saya terbiasa dengan kode bersih dalam hal sasaran yang ditetapkan oleh OOP + SOLID. Saya bertanya-tanya apakah ada seperangkat prinsip desain yang bekerja dengan OOP tetapi sepenuhnya atau sebagian tidak kompatibel dengan SOLID.
MetaFight

1
Saya jauh lebih khawatir tentang kinerja permainan Anda itu daripada tentang hal lain. Kecuali kita berbicara tentang game berbasis teks.
Euforia

1
Game ini pada dasarnya adalah sebuah eksperimen. Saya mencoba berbagai pendekatan pengembangan, dan saya juga mencoba melihat apakah kode enterprise-y dapat berfungsi saat menulis permainan. Saya agak berharap kinerja menjadi masalah, tetapi belum. Jika / ketika itu terjadi, maka hal berikutnya yang akan saya coba adalah bagaimana mengadaptasi kode saya menjadi kode pemain. Banyak belajar!
MetaFight

1
Dengan risiko beberapa downvotes, saya ingin kembali ke pertanyaan "mengapa": OOP dalam pengembangan game . Karena sifat pengembangan gim, OOP "tradisional" tampaknya tidak disukai karena mendukung berbagai rasa sistem entitas / komponen, varian yang menarik di sini misalnya. Ini agak membuat saya berpikir bahwa pertanyaannya seharusnya bukan "SOLID + OOP" melainkan, "OOP + Pengembangan Game". Ketika Anda melihat grafik interaksi objek untuk pengembang game. vs. katakanlah aplikasi N-tier, perbedaan mungkin berarti paradigma baru baik.
J Trana

Jawaban:


20

Apakah ada kasus di OOP di mana beberapa atau semua prinsip SOLID tidak meminjamkan diri untuk membersihkan kode?

Secara umum, tidak. Sejarah telah menunjukkan bahwa prinsip-prinsip SOLID sebagian besar berkontribusi pada peningkatan decoupling, yang pada gilirannya telah terbukti meningkatkan fleksibilitas dalam kode dan dengan demikian kemampuan Anda untuk mengakomodasi perubahan serta membuat kode lebih mudah untuk dipikirkan, diuji, digunakan kembali .. singkatnya, buat kode Anda lebih bersih.

Sekarang, mungkin ada kasus di mana prinsip-prinsip SOLID bertabrakan dengan KERING (jangan ulangi sendiri), CIUMAN (tetap bodoh, sederhana) atau prinsip-prinsip lain dari desain OO yang baik. Dan tentu saja, mereka dapat berbenturan dengan realitas persyaratan, keterbatasan manusia, keterbatasan bahasa pemrograman kita, hambatan lainnya.

Singkatnya, prinsip-prinsip SOLID akan selalu meminjamkan diri untuk membersihkan kode, tetapi dalam beberapa skenario mereka akan meminjamkan diri mereka sendiri kurang dari alternatif yang saling bertentangan. Mereka selalu baik, tetapi kadang-kadang hal lain lebih baik.


14
Saya suka tapi kadang-kadang hal lain lebih baik . Itu memiliki "Peternakan Hewan" merasa untuk itu. ;)
FrustratedWithFormsDesigner

12
+1 Ketika saya baru saja melihat prinsip-prinsip SOLID, saya melihat 5 "baik untuk dimiliki". Tapi tidak satu pun dari mereka yang KERING, KISS, dan "jelaskan niat Anda". Gunakan SOLID, tapi tunjukkan hati-hati dan skeptis.
user949300

Saya setuju. Saya pikir prinsip yang benar untuk diikuti jelas tergantung pada situasi, tetapi selain dari persyaratan kinerja yang keras, yang selalu berada di atas segalanya (ini penting terutama dalam pengembangan game), saya cenderung berpikir KERING dan KISS biasanya lebih penting dari SOLID. Tentu saja, semakin bersih Anda membuat kode menjadi lebih baik, jadi jika dapat mengikuti semua prinsip tanpa konflik, semakin baik.
Ben Lee

Bagaimana dengan pemisahan integrasi vs. desain agregat yang efisien? Jika antarmuka "urutan" dasar [mis. IEnumerable] mencakup metode seperti Countdan asImmutable, dan properti seperti getAbilities[yang kembalinya akan menunjukkan apakah hal-hal seperti Countakan "efisien"], maka seseorang dapat memiliki metode statis yang mengambil banyak urutan dan menggabungkannya sehingga mereka akan berperilaku sebagai urutan yang lebih panjang. Jika kemampuan tersebut hadir dalam tipe urutan dasar, bahkan jika mereka hanya rantai ke implementasi standar, maka agregat akan dapat mengekspos kemampuan itu ...
supercat

... dan mengimplementasikannya secara efisien ketika digunakan dengan kelas dasar yang dapat melakukannya. Jika seseorang mengumpulkan sepuluh juta daftar item yang mengetahui hitungannya, dan daftar lima item yang hanya bisa mengambil item secara berurutan, permintaan entri 10.000,003 harus membaca hitungan dari daftar pertama, dan kemudian membaca tiga item dari yang kedua . Antarmuka kitchen-sink mungkin melanggar ISP, tetapi mereka bisa sangat meningkatkan kinerja mereka dari beberapa skenario komposisi / agregasi.
supercat

13

Saya pikir saya memiliki perspektif yang tidak biasa dalam hal saya telah bekerja di bidang keuangan dan game.

Banyak programmer yang saya temui dalam permainan sangat buruk di Rekayasa Perangkat Lunak - tetapi mereka tidak perlu berlatih seperti SOLID. Secara tradisional, mereka memasukkan permainan mereka ke dalam kotak - kemudian mereka selesai.

Di bidang keuangan, saya menemukan bahwa pengembang dapat menjadi sangat ceroboh dan tidak disiplin karena mereka tidak memerlukan kinerja yang Anda lakukan dalam permainan.

Kedua pernyataan di atas tentu saja over-generalisasi, tetapi sebenarnya, dalam kedua kasus kode bersih sangat penting. Untuk optimasi kode yang jelas dan mudah dipahami adalah penting. Demi pemeliharaan, Anda menginginkan hal yang sama.

Itu tidak berarti bahwa SOLID bukan tanpa kritik. Mereka disebut prinsip namun mereka harus diikuti seperti pedoman? Jadi kapan tepatnya saya harus mengikuti mereka? Kapan saya harus melanggar peraturan?

Anda mungkin dapat melihat dua potong kode dan mengatakan yang mana yang paling sesuai dengan SOLID. Namun secara terpisah, tidak ada cara obyektif untuk mengubah kode menjadi SOLID. Ini jelas untuk interpretasi pengembang.

Adalah tidak berurutan untuk mengatakan bahwa, "sejumlah besar kelas dapat mengarah pada mimpi buruk pemeliharaan". Namun, jika SRP tidak ditafsirkan dengan benar, Anda dapat dengan mudah berakhir dengan masalah ini.

Saya telah melihat kode dengan banyak lapisan yang tidak bertanggung jawab. Kelas yang tidak melakukan apa pun selain lulus dari satu kelas ke kelas lainnya. Masalah klasik terlalu banyak lapisan tipuan.

SRP jika disalahgunakan dapat berakhir dengan kurangnya kohesi dalam kode Anda. Anda dapat mengetahui hal ini saat Anda mencoba menambahkan fitur. Jika Anda selalu harus mengubah beberapa tempat secara bersamaan, maka kode Anda tidak memiliki kohesi.

Open-Closed bukan tanpa kritik. Misalnya, lihat ulasan Jon Skeet tentang prinsip Open Closed . Saya tidak akan mereproduksi argumennya di sini.

Argumen Anda tentang LSP tampaknya agak hipotetis dan saya tidak benar-benar mengerti apa yang Anda maksud dengan bentuk pewarisan aman lainnya?

ISP sepertinya menduplikasi SRP. Tentunya mereka harus ditafsirkan sama karena Anda tidak pernah dapat mengantisipasi apa yang akan dilakukan semua klien dari antarmuka Anda. Antarmuka kohesif hari ini adalah pelanggar ISP besok. Satu-satunya kesimpulan yang tidak masuk akal adalah memiliki tidak lebih dari satu anggota per antarmuka.

DIP dapat menyebabkan kode tidak dapat digunakan. Saya telah melihat kelas dengan sejumlah besar parameter dalam nama DIP. Kelas-kelas ini biasanya akan "baru" di bagian komposit mereka, tetapi saya nama-kemampuan uji kita tidak boleh menggunakan kata kunci baru lagi.

SOLID sendiri tidak cukup bagi Anda untuk menulis kode bersih. Saya telah melihat buku Robert C. Martin, " Clean Code: A Handbook of Agile Software Craftsmanship ". Masalah terbesar dengannya adalah mudahnya membaca buku ini dan menafsirkannya sebagai aturan. Jika Anda melakukannya, Anda tidak mengerti intinya. Ini berisi daftar besar aroma, heuristik dan prinsip - lebih dari sekadar lima dari SOLID. Semua 'prinsip' ini tidak benar-benar prinsip tetapi pedoman. Paman Bob mendeskripsikannya sendiri sebagai "lubang kecil" untuk konsep. Mereka adalah tindakan penyeimbang; mengikuti pedoman apa pun secara membabi buta akan menyebabkan masalah.


1
Jika Anda memiliki sejumlah besar parameter konstruktor, itu menunjukkan bahwa Anda telah melanggar SRP. Namun wadah DI menghilangkan rasa sakit yang terkait dengan sejumlah besar parameter konstruktor, jadi saya tidak setuju dengan pernyataan Anda tentang DIP.
Stephen

Semua 'prinsip' ini tidak benar-benar prinsip tetapi pedoman. Mereka adalah tindakan penyeimbang; seperti yang saya katakan di posting saya setelah SRP terlalu ekstrim itu sendiri bisa bermasalah.
Dave Hillier

Jawaban bagus, +1. Satu hal yang ingin saya tambahkan: Saya membaca ulasan Skeet lebih banyak kritik terhadap definisi buku teks standar OCP, bukan ide-ide inti di balik OCP. Untuk pemahaman saya, ide variasi yang dilindungi adalah apa yang seharusnya OCP dari tempat pertama.
Doc Brown

5

Inilah pendapat saya:

Meskipun prinsip-prinsip SOLID bertujuan untuk basis kode yang tidak berlebihan dan fleksibel, ini mungkin merupakan trade-off dalam keterbacaan dan pemeliharaan jika ada terlalu banyak kelas dan lapisan.

Ini terutama tentang jumlah abstraksi . Sejumlah besar kelas mungkin ok jika didasarkan pada beberapa abstraksi. Karena kami tidak tahu ukuran dan spesifik proyek Anda, sulit untuk mengatakannya, tetapi sedikit mengkhawatirkan bahwa Anda menyebutkan beberapa layer selain sejumlah besar kelas.

Saya kira prinsip-prinsip SOLID tidak tidak setuju dengan OOP, tetapi masih mungkin untuk menulis kode yang tidak bersih mengikuti mereka.


3
+1 Secara definisi hampir, sistem yang sangat fleksibel harus kurang dapat dirawat daripada yang kurang fleksibel. Fleksibilitas dikenakan biaya karena kompleksitas tambahan yang dibawanya.
Andy

@Andy belum tentu. pada pekerjaan saya saat ini, banyak bagian terburuk dari kode ini berantakan karena hanya dilempar bersama dengan "melakukan sesuatu yang sederhana, hanya meretasnya dan membuatnya bekerja sehingga tidak menjadi terlalu rumit". Akibatnya, banyak bagian dari sistem yang 100% kaku. Anda tidak dapat memodifikasinya sama sekali tanpa menulis ulang sebagian besar dari mereka. Sangat sulit untuk mempertahankannya. Maintainability berasal dari kohesi tinggi dan kopling rendah, bukan dari seberapa fleksibel sistemnya.
sara

3

Pada hari-hari awal saya berkembang, saya mulai menulis Roguelike di C ++. Karena saya ingin menerapkan metodologi berorientasi objek yang baik yang saya pelajari dari pendidikan saya, saya langsung melihat item permainan sebagai objek C ++. Potion, Swords, Food, Axe, JavelinsDll semua berasal dari inti dasar Itemkode, penanganan nama, ikon, berat badan, hal semacam itu.

Kemudian, saya membuat kode logika tas, dan menghadapi masalah. Saya dapat memasukkannya ke Itemsdalam tas, tetapi kemudian, jika saya memilih satu, bagaimana saya tahu apakah itu a Potionatau a Sword? Saya mencari di Internet bagaimana cara menurunkan barang. Saya meretas opsi kompiler untuk mengaktifkan informasi runtime sehingga saya akan tahu apa Itemtipe sebenarnya saya yang abstrak , dan mulai beralih logika pada jenis untuk mengetahui operasi apa yang akan saya izinkan (misalnya menghindari minum Swordsdan memakai Potionskakiku)

Itu pada dasarnya mengubah kode dalam bola besar lumpur setengah copypasted. Seiring berjalannya proyek, saya mulai mengerti apa itu kegagalan desain. Apa yang terjadi adalah saya mencoba menggunakan kelas untuk merepresentasikan nilai. Hirarki kelas saya bukanlah abstraksi yang bermakna. Apa yang saya harus lakukan adalah membuat Itemmenerapkan satu set fungsi, seperti Equip (), Apply (), dan Throw (), dan membuat setiap berperilaku berdasarkan nilai-nilai. Menggunakan enum untuk mewakili slot peralatan. Menggunakan enum untuk mewakili jenis senjata yang berbeda. Menggunakan lebih banyak nilai dan lebih sedikit pengelompokan, karena subklasifikasi saya tidak memiliki tujuan lain selain untuk mengisi nilai akhir ini.

Karena Anda menghadapi multiplikasi objek di bawah lapisan abstraksi, saya pikir wawasan saya dapat berharga di sini. Jika Anda tampaknya memiliki terlalu banyak jenis objek, mungkin Anda bingung jenis apa yang seharusnya dengan apa yang seharusnya menjadi nilai.


2
Masalahnya adalah tipe-tipe yang membingungkan dengan kelas-kelas nilai (catatan: Saya tidak menggunakan kelas kata untuk merujuk pada gagasan OOP / C ++ tentang kelas.) Ada lebih dari satu cara untuk mewakili suatu titik dalam bidang kartesius (koordinat persegi panjang, kutub Koordinat) tetapi mereka semua adalah bagian dari tipe yang sama. Namun bahasa OOP mainstream tidak mendukung klasifikasi nilai secara alami. Hal yang paling dekat dengan itu adalah penyatuan C / C ++, tetapi Anda perlu menambahkan bidang tambahan hanya untuk mengetahui apa yang Anda masukkan di sana.
Doval

4
Pengalaman Anda dapat digunakan sebagai anti-contoh. Dengan tidak menggunakan SOLID atau metodologi pemodelan apa pun, Anda membuat kode yang tidak dapat dipertahankan dan tidak dapat dipertahankan.
Euforia

1
@ Euforia saya berusaha keras. Saya memiliki metodologi berorientasi objek. Ada perbedaan antara memiliki metodologi dan berhasil menerapkannya :)
Arthur Havlicek

2
Bagaimana ini contoh prinsip SOLID yang berjalan bertentangan dengan kode bersih di OOP? Tampaknya lebih seperti contoh desain yang salah - ini adalah ortogonal untuk OOP!
Andres F.

1
Kelas ITEM dasar Anda harus memiliki sejumlah metode boolean "canXXXX" yang semuanya default ke FALSE. Anda kemudian dapat mengatur "canThrow ()" untuk mengembalikan true di kelas Javelin Anda, dan canEat () untuk mengembalikan true untuk kelas Postion Anda.
James Anderson

3

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):

masukkan deskripsi gambar di sini

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:

masukkan deskripsi gambar di sini

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 PointLighttidak 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.

masukkan deskripsi gambar di sini

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).

  1. Ya, ada prinsip-prinsip desain OOP yang sebagian bertentangan dengan SOLID
  2. 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 PhysicsSystematau RenderSystematau 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):

masukkan deskripsi gambar di sini

... untuk ini di mana hanya sistem yang memiliki fungsionalitas (komponen biru sekarang hanya data, dan sekarang ini adalah diagram kopling):

masukkan deskripsi gambar di sini

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,


Saya suka perbedaan antara interaksi dan kopling. Meskipun diagram interaksi pertama Anda dikaburkan. Ini adalah grafik planar, jadi tidak perlu lagi untuk melintas panah.
D Drmmr

2

Prinsip-prinsip SOLID baik, tetapi KISS dan YAGNI mengambil prioritas jika terjadi konflik. Tujuan SOLID adalah mengelola kompleksitas, tetapi jika menerapkan SOLID itu sendiri membuat kode lebih kompleks maka ia mengalahkan tujuannya. Sebagai contoh, jika Anda memiliki program kecil dengan hanya beberapa kelas, menerapkan DI, pemisahan antarmuka, dll. Mungkin akan sangat meningkatkan kompleksitas keseluruhan program.

Prinsip terbuka / tertutup sangat kontroversial. Pada dasarnya ia mengatakan Anda harus menambahkan fitur ke kelas dengan membuat subclass atau implementasi terpisah dari antarmuka yang sama, tetapi membiarkan kelas asli tidak tersentuh. Jika Anda mengelola perpustakaan atau layanan yang digunakan oleh klien yang tidak terkoordinasi, ini masuk akal, karena Anda tidak ingin menghentikan perilaku yang bergantung pada klien yang keluar. Namun jika Anda memodifikasi kelas yang hanya digunakan di aplikasi Anda sendiri, seringkali akan jauh lebih sederhana dan bersih untuk hanya memodifikasi kelas.


Diskusi Anda tentang OCP mengabaikan kemungkinan memperluas perilaku kelas melalui komposisi (misalnya menggunakan objek strategi untuk memungkinkan algoritma yang digunakan oleh kelas untuk diubah), yang umumnya dianggap sebagai cara yang lebih baik untuk mematuhi kepala sekolah daripada subklasifikasi .
Jules

1
Jika KISS dan YAGNI selalu memprioritaskan Anda harus tetap mengkode dalam 1 dan 0. Perakit hanyalah hal lain yang bisa salah. Tidak KISS dan YAGNI menunjukkan dua dari sekian banyak biaya pekerjaan desain. Biaya itu harus selalu seimbang dengan manfaat pekerjaan desain apa pun. SOLID memiliki manfaat yang dalam beberapa kasus menebus biaya. Tidak ada cara sederhana untuk mengetahui kapan Anda telah melewati batas.
candied_orange

@CandiedOrange: Saya tidak setuju bahwa kode mesin lebih sederhana daripada kode dalam bahasa tingkat tinggi. Kode mesin akhirnya mengandung banyak kerumitan yang tidak disengaja karena kurangnya abstraksi, jadi jelas bukan KISS untuk memutuskan untuk menulis aplikasi khas dalam kode mesin.
JacquesB

@ Jules: Ya komposisi lebih disukai, tetapi masih mengharuskan Anda untuk merancang kelas asli sedemikian rupa sehingga komposisi dapat digunakan untuk menyesuaikannya, maka Anda harus membuat asumsi tentang aspek apa yang perlu disesuaikan di masa depan dan aspek apa tidak dapat disesuaikan. Dalam beberapa kasus ini perlu dilakukan (misalnya Anda menulis perpustakaan untuk distribusi) tetapi memiliki biaya, maka mengikuti SOLID tidak selalu optimal.
JacquesB

1
@ JacquesB - benar. Dan OCP pada intinya menentang YAGNI; namun Anda ingin mencapainya, itu mengharuskan Anda untuk merancang titik ekstensi untuk memungkinkan peningkatan di kemudian hari. Menemukan titik keseimbangan yang cocok antara kedua cita-cita itu ... rumit.
Jules

1

Dalam pengalaman saya: -

Kelas besar secara eksponensial lebih sulit dipertahankan karena kelasnya semakin besar.

Kelas kecil lebih mudah dipelihara dan kesulitan mempertahankan koleksi kelas kecil meningkat secara aritmatik ke jumlah kelas.

Terkadang kelas besar tidak bisa dihindari masalah besar terkadang membutuhkan kelas besar, tetapi, mereka jauh lebih sulit untuk dibaca dan dipahami. Untuk kelas kecil yang lebih baik namanya, nama saja sudah cukup untuk pemahaman Anda bahkan tidak perlu melihat kode kecuali ada masalah yang sangat spesifik dengan kelas tersebut.


0

Saya selalu merasa sulit untuk menarik garis antara 'S' solid dan (mungkin kuno) perlu membiarkan suatu objek memiliki semua pengetahuan tentang dirinya sendiri.

15 tahun yang lalu saya bekerja dengan pengembang Deplhi yang memiliki cawan suci mereka untuk memiliki kelas yang berisi segalanya. Jadi untuk hipotek Anda dapat menambahkan asuransi dan pembayaran serta pendapatan dan pinjaman serta info pajak dan Anda dapat menghitung pajak yang harus dibayar, pajak yang harus dikurangi dan dapat membuat serial sendiri, bertahan sendiri di disk, dll.

Dan sekarang saya memiliki objek yang sama yang terbagi dalam banyak kelas yang lebih kecil yang memiliki keuntungan bahwa mereka dapat diuji unit lebih mudah dan lebih baik tetapi tidak memberikan gambaran yang baik dari seluruh model bisnis.

Dan ya saya tahu Anda tidak ingin mengikat objek Anda ke database tetapi Anda bisa menyuntikkan repositori di kelas hipotek besar untuk menyelesaikannya.

Selain itu tidak selalu mudah untuk menemukan nama yang bagus untuk kelas yang hanya melakukan hal kecil.

Jadi saya tidak yakin tentang 'S' selalu merupakan ide yang bagus.

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.