Його стурбованість полягала в тому, що велика кількість класів перетвориться на кошмар технічного обслуговування. Моя думка полягала в тому, що це матиме прямо протилежний ефект.
Я абсолютно на боці вашого друга, але це може бути питанням наших доменів та типів проблем та дизайну, з якими ми вирішуємось, і особливо, які типи речей, можливо, потребуватимуть змін у майбутньому. Різні проблеми, різні рішення. Я не вірю в правильно чи неправильно, просто програмісти намагаються знайти найкращий спосіб, щоб найкраще вирішити свої конкретні проблеми дизайну. Я працюю в VFX, який не надто на відміну від ігрових двигунів.
Але проблема для мене, з якою я боровся з тим, що, принаймні, можна назвати дещо більш відповідною до СОЛІД архітектури (вона базується на COM), може бути грубо зведена до "занадто багато класів" або "занадто багато функцій", як ваш друг може описати. Я б конкретно сказав: "занадто багато взаємодій, занадто багато місць, які могли б погано поводитись, занадто багато місць, які могли б викликати побічні ефекти, занадто багато місць, які можуть змінитись, і занадто багато місць, які не можуть робити те, що ми думаємо, що вони роблять . "
У нас було декілька абстрактних (і чистих) інтерфейсів, реалізованих навантаженнями підтипів, як це було зроблено (зроблено цю діаграму в контексті розмови про переваги ECS, ігноруйте коментар внизу зліва):
Там, де інтерфейс руху або інтерфейс вузла сцени можуть бути реалізовані сотнями підтипів: світильниками, камерами, сітками, фізичними вирішувачами, шейдерами, текстурами, кістками, примітивними формами, кривими тощо тощо (а часто було декілька типів кожного ). І головна проблема полягала в тому, що ці конструкції не були настільки стійкими. У нас були вимоги, що змінюються, і іноді самі інтерфейси доводилося змінювати, і коли ви хочете змінити абстрактний інтерфейс, реалізований 200 підтипами, це надзвичайно дорога зміна. Ми почали пом'якшувати, що, використовуючи абстрактні базові класи, між якими знижувались витрати на такі зміни дизайну, але вони все ще були дорогими.
Тож, як альтернатива, я почав досліджувати структуру системної системи, яка використовується досить часто в ігровій індустрії. Це змінило все, щоб бути таким:
І вау! Це була така різниця в плані ремонту. Залежності вже не тягнулися до абстракцій , а до даних (компонентів). І в моєму випадку, принаймні, дані були набагато стійкішими та легшими підходити до дизайну наперед, незважаючи на зміни вимог (хоча те, що ми можемо зробити з тими ж даними, постійно змінюється зі зміною вимог).
Крім того, оскільки суб'єкти в ECS використовують склад замість спадкування, їм фактично не потрібно містити функціональність. Вони є лише аналогічним «контейнером компонентів». Це зробило так, що аналогічні 200 підтипів, які реалізували інтерфейс руху, перетворюються на 200 екземплярів сутності (а не окремі типи з окремим кодом), які просто зберігають компонент руху (що є не що інше, як дані, пов'язані з рухом). A PointLight
більше не є окремим класом / підтипом. Це зовсім не клас. Це екземпляр сутності, який просто поєднує деякі компоненти (дані), пов’язані з тим, де він знаходиться в просторі (рух) та специфічними властивостями точкових вогнів. Єдиний функціонал, пов'язаний з ними, знаходиться всередині систем, як-отRenderSystem
, який шукає легких компонентів у сцені, щоб визначити, як відобразити сцену.
Із зміною вимог підходу до ECS часто виникала потреба лише змінити одну або дві системи, що працюють на цих даних, або просто запровадити нову систему на стороні, або ввести новий компонент, якщо потрібні нові дані.
Так що для мого домену, принаймні, і я майже впевнений, що це не для всіх, це набагато полегшило ситуацію, оскільки залежність протікала до стабільності (речі, які взагалі не потрібно часто змінювати). Це не було в архітектурі COM, коли залежності рівномірно протікали до абстракцій. У моєму випадку набагато простіше розібратися, які дані потрібні для руху вперед, а не всі можливі речі, які ти можеш зробити з цим, що часто змінюється протягом місяців або років, коли виникають нові вимоги.
Чи є випадки в OOP, коли деякі або всі принципи SOLID не піддаються очищенню коду?
Ну, чистий код я не можу сказати, оскільки деякі люди прирівнюють чистий код до SOLID, але, безумовно, є випадки, коли відокремлення даних від функціональності, як це робиться в ECS, і перенаправлення залежностей від абстракцій до даних, безумовно, можуть значно спростити речі змінити з очевидних причин зв'язку, якщо дані будуть набагато стійкішими, ніж абстракції. Звичайно залежність від даних може утруднювати підтримку інваріантів, але ECS прагне пом'якшити це до мінімуму із системною організацією, яка мінімізує кількість систем, які отримують доступ до будь-якого типу компонентів.
Не обов'язково залежності залежати від абстракцій, як це підказує DIP; залежність повинна спрямовуватися на речі, які навряд чи потребують майбутніх змін. Це можуть бути або не бути абстракціями у всіх випадках (це, звичайно, не було у мене).
- Так, існують принципи проектування OOP, які частково суперечать SOLID
- Так, існують принципи проектування OOP, які повністю суперечать SOLID.
Я не впевнений, чи справді ECS є ароматом OOP. Деякі люди визначають це таким чином, але я вважаю це дуже різним, що пов'язано з характеристиками зв'язку та відокремленням даних (компонентів) від функціональності (системи) та відсутністю інкапсуляції даних. Якщо це вважати формою ООП, я вважаю, що це дуже суперечить SOLID (принаймні, найсуворіші ідеї SRP, відкриті / закриті, заміни лисков і DIP). Але я сподіваюсь, що це розумний приклад одного випадку та сфери, де найбільш фундаментальні аспекти SOLID, принаймні, як люди взагалі інтерпретують їх у більш розпізнаваному контексті OOP, можуть бути не так застосовні.
Заняття підлітками
Я пояснював архітектуру однієї з моїх ігор, яка, на подив мого друга, містила багато невеликих класів та кілька шарів абстракції. Я стверджував, що це був результат, коли я зосередився на наданні всього єдиної відповідальності, а також на послаблення зв'язку між компонентами.
ECS сильно кинув виклик і змінив мої погляди. Як і ви, я думав, що сама ідея ремонтопридатності - це найпростіша реалізація можливих речей, що передбачає багато речей, і, крім того, багато взаємозалежних речей (навіть якщо взаємозалежності знаходяться між абстракціями). Це має найбільш сенс, якщо ви збільшуєте масштаб лише одного класу чи функції, щоб побачити найпростішу і просту реалізацію, а якщо ми її не бачимо, перефактуруйте її і, можливо, навіть розкладіть її далі. Але можна легко пропустити те, що відбувається із зовнішнім світом у результаті, тому що щоразу, коли ви розділите щось відносно складне на 2 і більше речей, ці 2 або більше речей повинні неминуче взаємодіяти * (див. Нижче) один з одним у деяких Таким чином, або щось зовні має взаємодіяти з усіма ними.
Цього дня я виявляю, що між простотою чогось і тим, скільки речей є, і тим, скільки потрібно взаємодії, існує балансуючий акт. Системи в якості ECS мають тенденцію бути досить неабияким з нетривіальними реалізаціями для роботи на даних, як PhysicsSystem
або RenderSystem
або GuiLayoutSystem
. Однак той факт, що складний продукт потребує такої малої кількості, має тенденцію полегшити крок назад і міркувати про загальну поведінку всієї бази коду. Там є щось, що може підказати, що це може бути не поганою ідеєю спертися на сторону менших, об'ємних класів (все ще виконують, мабуть, особливу відповідальність), якщо це означає менше класів для підтримки та міркувань, а також менше взаємодій протягом усієї система.
Взаємодії
Я кажу "взаємодія", а не "з'єднання" (хоча зменшення взаємодій означає зменшення обох), оскільки ви можете використовувати абстракції для роз'єднання двох конкретних об'єктів, але вони все одно спілкуються один з одним. Вони все ще могли викликати побічні ефекти в процесі цього непрямого спілкування. І часто я вважаю, що моя здатність міркувати про правильність системи пов'язана з цими "взаємодіями" більше, ніж з "з'єднанням". Мінімізація взаємодій, як правило, набагато полегшує мені міркування про все з виду пташиного польоту. Це означає, що речі взагалі не розмовляють між собою, і з цього сенсу ECS також має тенденцію дійсно мінімізувати "взаємодію", а не просто з'єднання, з найменшими мінімумами (принаймні, у мене немає "
Однак це могло бути принаймні частково мені та моїм особистим недолікам. Я знайшов для мене найбільшу перешкоду створювати системи величезних масштабів і все ще впевнено міркувати про них, переходити по них і відчувати, що я можу вносити будь-які потенційні бажані зміни в будь-якому місці передбачуваним способом, - це управління державою та ресурсами разом з побічні ефекти. Це найбільша перешкода, яка починає виникати, коли я переходжу від десятків тисяч LOC до сотень тисяч LOC до мільйонів LOC, навіть для коду, який я повністю створив самостійно. Якщо щось буде сповільнювати мене до повзання понад усе, це сенс, що я більше не можу зрозуміти, що відбувається в частині стану застосування, даних, побічних ефектів. Це ' s - це не той робототехнічний час, який потрібен для внесення змін, що сповільнює мене настільки, як невміння зрозуміти повний вплив змін, якщо система зростає поза здатністю мого розуму міркувати про це. І скорочення взаємодій було для мене найефективнішим способом дозволити продукту розростатись значно більшими можливостями без того, щоб я особисто перевантажувався цими речами, оскільки скорочення взаємодій до мінімуму також зменшує кількість місць, які можуть навіть можливо змінити стан застосування та спричинити істотні побічні ефекти.
Це може перетворити щось подібне (де все на діаграмі має функціональність, і, очевидно, сценарій у реальному світі мав би багато-багато разів кількість об'єктів. Це діаграма "взаємодії", а не з'єднання, як з'єднання між ними будуть абстракції):
... до цього лише ті системи, які мають функціональні можливості (сині компоненти тепер просто дані, а тепер це схема з’єднання):
І з цього приводу виникають думки, і, можливо, спосіб створити деякі з цих переваг у більш відповідному контексті OOP, який є більш сумісним із SOLID, але я ще не зовсім знайшов конструкції та слова, і я вважаю важко, оскільки термінологія, якою я звик перекидати все, що стосується безпосередньо ООП. Я намагаюся зрозуміти це, читаючи відповіді людей тут, а також намагаюся сформулювати свої власні, але є деякі речі дуже цікаві щодо природи ECS, які я не зміг ідеально поставити палець на це може бути більш широко застосовно навіть до архітектур, які не використовують його. Я також сподіваюсь, що ця відповідь не прозвучить як акція ECS! Мені просто здається, що це дуже цікаво, оскільки проектування ECS дуже змінило мою думку,