Dla mnie jest to problem sprzężenia i związany z szczegółowością projektu. Nawet najbardziej luźna forma sprzęgania wprowadza zależności między rzeczami. Jeśli jest to zrobione dla setek do tysięcy obiektów, nawet jeśli wszystkie są względnie proste, zastosuj się do SRP, a nawet jeśli wszystkie zależności płyną w kierunku stabilnych abstrakcji, powstanie podstawa kodu, którą bardzo trudno uzasadnić jako wzajemnie powiązaną całość.
Istnieją praktyczne rzeczy, które pomagają ocenić złożoność bazy kodu, które nie są często omawiane w teoretycznej SE, jak na przykład, jak głęboko w stos wywołań można dostać się przed końcem i jak głęboko musisz zejść, zanim to możliwe, z bardzo pewny siebie, zrozum wszystkie możliwe działania niepożądane, które mogą wystąpić na tym poziomie stosu wywołań, w tym w przypadku wyjątku.
I z własnego doświadczenia wynika, że o wiele łatwiejsze jest rozumowanie bardziej płaskich systemów z płytszymi stosami wywołań. Skrajnym przykładem może być system podmiot-komponent, w którym komponenty są tylko surowymi danymi. Tylko systemy mają funkcjonalność, a podczas wdrażania i korzystania z ECS uznałem, że jest to najłatwiejszy system w historii, jak dotąd, do rozumienia, kiedy złożone bazy kodu obejmujące setki tysięcy linii kodu w zasadzie zagotowują się do kilkudziesięciu systemów, które zawierają całą funkcjonalność.
Zbyt wiele rzeczy zapewnia funkcjonalność
Alternatywą wcześniej, gdy pracowałem w poprzednich bazach kodów, był system z setkami do tysięcy przeważnie małych obiektów, w przeciwieństwie do kilkudziesięciu dużych systemów z niektórymi obiektami używanymi tylko do przekazywania wiadomości z jednego obiektu do drugiego ( Message
np. Obiekt, który miał swój własny interfejs publiczny). Zasadniczo to uzyskuje się analogicznie po przywróceniu ECS z powrotem do punktu, w którym komponenty mają funkcjonalność, a każda unikalna kombinacja komponentów w encji daje własny typ obiektu. I to będzie miało tendencję do uzyskiwania mniejszych, prostszych funkcji odziedziczonych i zapewnianych przez nieskończone kombinacje obiektów, które modelują pomniejsze pomysły (Particle
obiekt vs.Physics System
, np.). Jednak ma również tendencję do generowania złożonego wykresu wzajemnych zależności, który utrudnia rozumowanie o tym, co dzieje się z poziomu ogólnego, po prostu dlatego, że w bazie kodu jest tak wiele rzeczy, które mogą faktycznie coś zrobić, a zatem mogą zrobić coś złego - - typy, które nie są typami „danych”, ale typami „obiektów” z powiązaną funkcjonalnością. Typy, które służą jako czyste dane bez powiązanej funkcjonalności, nie mogą pójść źle, ponieważ same nie mogą nic zrobić.
Czyste interfejsy nie pomagają tak bardzo w problemie ze zrozumieniem, ponieważ nawet jeśli to sprawia, że „zależności kompilacji w czasie” są mniej skomplikowane i zapewnia więcej miejsca na zmiany i ekspansję, nie czyni to „zależności środowiska wykonawczego” i interakcji mniej skomplikowanymi. Obiekt klienta nadal wywołuje funkcje na konkretnym obiekcie konta, nawet jeśli są wywoływane IAccount
. Polimorfizm i abstrakcyjne interfejsy mają swoje zastosowania, ale nie rozdzielają rzeczy w sposób, który naprawdę pomaga w uzasadnieniu wszystkich skutków ubocznych, które mogą wystąpić w danym momencie. Aby osiągnąć ten rodzaj skutecznego oddzielenia, potrzebujesz bazy kodu, która zawiera znacznie mniej elementów zawierających funkcjonalność.
Więcej danych, mniej funkcjonalności
Dlatego uważam, że podejście ECS, nawet jeśli nie zastosujesz go całkowicie, jest niezwykle pomocne, ponieważ zamienia to, co byłyby setkami obiektów, w surowe dane dzięki nieporęcznym systemom, bardziej grubo zaprojektowanym, które zapewniają wszystkie funkcjonalność. Maksymalizuje liczbę typów „danych” i minimalizuje liczbę typów „obiektów”, a zatem absolutnie minimalizuje liczbę miejsc w systemie, które mogą się nie udać. Rezultatem końcowym jest bardzo „płaski” system bez złożonego wykresu zależności, tylko systemy do komponentów, nigdy odwrotnie, i nigdy do innych komponentów. Zasadniczo jest to o wiele więcej surowych danych i znacznie mniej abstrakcji, co skutkuje scentralizowaniem i spłaszczeniem funkcjonalności bazy kodu do kluczowych obszarów, kluczowych abstrakcji.
30 prostszych rzeczy niekoniecznie musi być prostszych do uzasadnienia niż jedna bardziej złożona rzecz, jeśli te 30 prostszych rzeczy są ze sobą powiązane, podczas gdy złożona rzecz stoi sama. Tak więc moją propozycją jest przeniesienie złożoności z interakcji między obiektami i bardziej na bardziej masywne obiekty, które nie muszą wchodzić w interakcje z niczym innym, aby osiągnąć masowe oddzielenie, do całych „systemów” (nie monolitów i boskich obiektów, pamiętajcie o tym i nie klasy z 200 metodami, ale coś znacznie wyższego poziomu niż a Message
lub a Particle
pomimo posiadania minimalistycznego interfejsu). I faworyzuj bardziej proste, stare typy danych. Im bardziej na nich polegasz, tym mniej sprzężenia otrzymasz. Nawet jeśli jest to sprzeczne z niektórymi pomysłami na SE, okazało się, że to bardzo pomaga.