Niedawno miałem rozmowę z moim przyjacielem na temat OOP w tworzeniu gier wideo.
Wyjaśniałem architekturę jednej z moich gier, która, ku zaskoczeniu mojego przyjaciela, zawierała wiele małych klas i kilka warstw abstrakcji. Argumentowałem, że był to wynik mojego skupienia się na nadaniu wszystkim Jednej Odpowiedzialności, a także na poluzowaniu sprzężenia między komponentami.
Martwił go, że duża liczba zajęć przełoży się na koszmar utrzymania. Moim zdaniem byłby to dokładnie odwrotny skutek. Dyskutowaliśmy o tym przez kilka stuleci, ostatecznie zgadzając się nie zgodzić, mówiąc, że być może zdarzają się przypadki, w których SOLIDNE zasady i właściwe OOP tak naprawdę nie pasują do siebie.
Nawet wpis w Wikipedii na temat zasad SOLID mówi, że są to wytyczne, które pomagają w pisaniu łatwego do utrzymania kodu i że są one częścią ogólnej strategii zwinnego i adaptacyjnego programowania.
Moje pytanie brzmi:
Czy w OOP są przypadki, w których niektóre lub wszystkie zasady SOLID nie nadają się do czyszczenia kodu?
Mogę od razu wyobrazić sobie, że Zasada Zastępstwa Liskowa mogłaby być sprzeczna z innym smakiem bezpiecznego dziedziczenia. Innymi słowy, jeśli ktoś opracuje inny użyteczny wzorzec zaimplementowany w drodze dziedziczenia, jest całkiem możliwe, że LSP może być z nim w bezpośrednim konflikcie.
Czy są jeszcze inni? Być może niektóre typy projektów lub pewne platformy docelowe działają lepiej przy mniejszym podejściu SOLID?
Edytować:
Chciałbym tylko sprecyzować, że nie pytam, jak poprawić mój kod;) Jedynym powodem, dla którego wspomniałem o projekcie w tym pytaniu, było podanie małego kontekstu. Moje pytanie dotyczy OOP i zasad projektowania w ogóle .
Jeśli jesteś ciekawy mojego projektu, zobacz to .
Edycja 2:
Wyobraziłem sobie, że na to pytanie można odpowiedzieć na jeden z 3 sposobów:
- Tak, istnieją zasady projektowania OOP, które częściowo kolidują z SOLID
- Tak, istnieją zasady projektowania OOP, które są całkowicie sprzeczne z SOLID
- Nie, SOLID to kolana pszczoły i OOP na zawsze będzie z nią lepszy. Ale, jak w przypadku wszystkiego, nie jest to panaceum. Pij odpowiedzialnie.
Opcje 1 i 2 prawdopodobnie wygenerowałyby długie i interesujące odpowiedzi. Z drugiej strony wariant 3 byłby krótką, nieciekawą, ale ogólnie uspokajającą odpowiedzią.
Wydaje się, że zbliżamy się do opcji 3.
źródło
Odpowiedzi:
Ogólnie nie. Historia pokazała, że wszystkie zasady SOLID w dużej mierze przyczyniają się do zwiększonego oddzielania, co z kolei wykazało, że zwiększa elastyczność kodu, a tym samym zdolność do dostosowania się do zmian, a także ułatwia kodowanie uzasadnienia, testowania, ponownego użycia. w skrócie, uczyń swój kod czystszym.
Teraz mogą zdarzyć się przypadki, w których zasady SOLID zderzają się z SUCHEM (nie powtarzaj się), KISS (niech to będzie głupie) lub innymi zasadami dobrego projektowania OO. I oczywiście mogą kolidować z rzeczywistością wymagań, ograniczeniami ludzi, ograniczeniami naszych języków programowania, innymi przeszkodami.
Krótko mówiąc, zasady SOLID zawsze nadadzą się do czyszczenia kodu, ale w niektórych scenariuszach nadadzą się mniej niż sprzeczne alternatywy. Zawsze są dobre, ale czasem inne rzeczy są bardziej dobre.
źródło
Count
iasImmutable
oraz właściwości podobnegetAbilities
[których zwrot wskazywałby, czy rzeczy takieCount
będą „wydajne”], wówczas można zastosować metodę statyczną, która pobiera wiele sekwencji i agreguje je, aby Będę się zachowywać jak pojedyncza dłuższa sekwencja. Jeśli takie umiejętności są obecne w podstawowym typie sekwencji, nawet jeśli łączą jedynie domyślne implementacje, agregat będzie w stanie odsłonić te umiejętności ...Myślę, że mam niezwykłą perspektywę, ponieważ pracowałem zarówno w finansach, jak i grach.
Wielu programistów, których spotkałem w grach, było okropnych w inżynierii oprogramowania - ale nie potrzebowali praktyk takich jak SOLID. Tradycyjnie wkładają swoją grę do pudełka - i gotowe.
W finansach zauważyłem, że programiści mogą być naprawdę niechlujni i niezdyscyplinowani, ponieważ nie potrzebują wydajności, którą osiągasz w grach.
Obie powyższe instrukcje są oczywiście nadmiernymi uogólnieniami, ale w obu przypadkach czysty kod ma kluczowe znaczenie. Dla optymalizacji niezbędny jest przejrzysty i łatwy do zrozumienia kod. Ze względu na łatwość konserwacji chcesz tego samego.
Nie oznacza to, że SOLID nie jest bez krytyki. Nazywa się je zasadami, a mimo to należy ich przestrzegać jak wskazówek? Kiedy dokładnie powinienem ich przestrzegać? Kiedy powinienem złamać zasady?
Prawdopodobnie mógłbyś spojrzeć na dwa fragmenty kodu i powiedzieć, który z nich najlepiej odpowiada SOLID. Ale w izolacji nie ma obiektywnego sposobu na przekształcenie kodu w SOLID. Zdecydowanie zależy to od interpretacji dewelopera.
Mówienie, że „duża liczba klas może prowadzić do koszmaru utrzymania” jest non sekitur. Jeśli jednak SRP nie zostanie poprawnie zinterpretowany, możesz łatwo skończyć z tym problemem.
Widziałem kod z wieloma warstwami praktycznie bez żadnej odpowiedzialności. Klasy, które nie robią nic poza przejściem z jednej klasy do drugiej. Klasyczny problem zbyt wielu warstw pośrednich.
W przypadku nadużycia SRP może skończyć się brakiem spójności w kodzie. Możesz to powiedzieć podczas próby dodania funkcji. Jeśli zawsze musisz zmieniać kilka miejsc w tym samym czasie, kodowi brakuje spójności.
Open-Closed nie jest pozbawiony krytyków. Na przykład zobacz recenzję Jona Skeeta dotyczącą zasady otwartego zamknięcia . Nie będę tu powtarzał jego argumentów.
Twój argument na temat LSP wydaje się raczej hipotetyczny i naprawdę nie rozumiem, co masz na myśli, mówiąc o innej formie bezpiecznego dziedziczenia?
Wydaje się, że dostawca usług internetowych nieco powiela SRP. Z pewnością należy je interpretować tak samo, jak nigdy nie można przewidzieć, co zrobią wszyscy klienci Twojego interfejsu. Dzisiejszy spójny interfejs jest jutrzejszym naruszającym ISP. Jedynym nielogicznym wnioskiem jest posiadanie nie więcej niż jednego członka na interfejs.
DIP może prowadzić do bezużytecznego kodu. Widziałem klasy o ogromnej liczbie parametrów w nazwie DIP. Te klasy zwykle „unowocześniały” swoje części kompozytowe, ale ja, biorąc pod uwagę zdolność testowania, nie możemy już nigdy używać nowego słowa kluczowego.
Sam SOLID nie wystarcza do napisania czystego kodu. Widziałem książkę Roberta C. Martina „ Clean Code: A Handbook of Agile Software Craftsmanship ”. Największym problemem jest to, że łatwo jest przeczytać tę książkę i zinterpretować ją jako regułę. Jeśli to zrobisz, tracisz sens. Zawiera dużą listę zapachów, heurystyki i zasad - znacznie więcej niż tylko pięć z SOLID. Wszystkie te „zasady” nie są tak naprawdę zasadami, ale wytycznymi. Wujek Bob sam opisuje je jako „szuflady” dla pojęć. Są aktem równoważącym; ślepe stosowanie się do jakichkolwiek wytycznych spowoduje problemy.
źródło
Oto moja opinia:
Chociaż zasady SOLID mają na celu zapewnienie nie redundantnej i elastycznej bazy kodu, może to być kompromis w zakresie czytelności i konserwacji, jeśli istnieje zbyt wiele klas i warstw.
Chodzi przede wszystkim o liczbę abstrakcji . Duża liczba klas może być w porządku, jeśli są one oparte na kilku abstrakcjach. Ponieważ nie znamy rozmiaru i specyfiki twojego projektu, trudno powiedzieć, ale trochę niepokojące jest to, że wspominasz o kilku warstwach oprócz dużej liczby klas.
Wydaje mi się, że zasady SOLID nie są niezgodne z OOP, ale nadal można pisać przylegający do nich kod.
źródło
We wczesnych latach rozwoju zacząłem pisać Roguelike w C ++. Ponieważ chciałem zastosować dobrą metodologię obiektową, której nauczyłem się z mojej edukacji, od razu zobaczyłem przedmioty w grze jako obiekty C ++.
Potion
,Swords
,Food
,Axe
,Javelins
Itd. Wszystko pochodzi od rdzenia podstawowegoItem
kodu, obsługi nazwa, ikona, wagi, tego rodzaju rzeczy.Następnie zakodowałem logikę torby i napotkałem problem. Mogę włożyć
Items
torbę, ale jeśli wybiorę jedną, to skąd mam wiedzieć, czy to jestPotion
aSword
? Sprawdziłem w Internecie, jak spuszczać przedmioty. Zhakowałem opcje kompilatora, aby włączyć informacje o środowisku wykonawczym, aby wiedzieć, jakie są moje abstrakcyjneItem
typy prawdziwe, i zacząłem przełączać logikę na typy, aby wiedzieć, na jakie operacje zezwalam (np. Unikaj piciaSwords
i postaw naPotions
nogi)Zasadniczo zmienił kod w dużą kulkę na wpół skopiowanego błota. W miarę realizacji projektu zacząłem rozumieć, co było błędem projektu. Stało się tak, że próbowałem używać klas do reprezentowania wartości. Moja hierarchia klasowa nie była znaczącą abstrakcją. Co ja powinienem był tego robić czyni
Item
wdrożyć szereg funkcji, takich jakEquip ()
,Apply ()
iThrow ()
, i by każdy zachowuje się w oparciu o wartości. Używanie wyliczeń do reprezentowania miejsc na wyposażenie. Używanie wyliczeń do reprezentowania różnych rodzajów broni. Używanie większej liczby wartości i mniejszej klasyfikacji, ponieważ moja podklasa nie miała innego celu niż wypełnienie tych ostatecznych wartości.Ponieważ stoisz w obliczu mnożenia obiektów pod warstwą abstrakcji, myślę, że mój wgląd może być tutaj cenny. Jeśli wydaje się, że masz zbyt wiele typów obiektów, być może mylisz, co powinno być typem z tym, co powinno być wartością.
źródło
Jestem absolutnie po stronie twojego przyjaciela, ale może to być kwestia naszych domen i rodzajów problemów i projektów, które rozwiązujemy, a zwłaszcza tego, jakie rodzaje rzeczy mogą wymagać zmian w przyszłości. Różne problemy, różne rozwiązania. Nie wierzę w dobro ani zło, po prostu programiści starają się znaleźć najlepszy sposób, aby najlepiej rozwiązać swoje problemy projektowe. Pracuję w VFX, który nie jest zbyt podobny do silników gier.
Ale problem, z którym borykam się w czymś, co można by nazwać nieco bardziej architekturą zgodną z SOLID (opartą na modelu COM), można z grubsza sprowadzić do „zbyt wielu klas” lub „zbyt wielu funkcji”, ponieważ twój przyjaciel może to opisać. Powiedziałbym konkretnie: „zbyt wiele interakcji, zbyt wiele miejsc, które mogą być niewłaściwie zachowane, zbyt wiele miejsc, które mogą powodować skutki uboczne, zbyt wiele miejsc, które mogą wymagać zmiany, i zbyt wiele miejsc, które mogą nie robić tego, co naszym zdaniem robią . ”
Mieliśmy garść abstrakcyjnych (i czystych) interfejsów zaimplementowanych przez mnóstwo różnych podtypów, takich jak ten (stworzyliśmy ten diagram w kontekście mówienia o korzyściach ECS, zignoruj lewy dolny komentarz):
Gdzie interfejs ruchu lub interfejs węzła sceny mogą być implementowane przez setki podtypów: światła, kamery, siatki, solwery fizyki, shadery, tekstury, kości, prymitywne kształty, krzywe itp. Itp. (I często istniało wiele rodzajów każdego z nich ). Ostatecznym problemem było to, że projekty te nie były tak stabilne. Mieliśmy zmieniające się wymagania, a czasem same interfejsy musiały się zmienić, a kiedy chcesz zmienić abstrakcyjny interfejs zaimplementowany przez 200 podtypów, jest to niezwykle kosztowna zmiana. Zaczęliśmy to łagodzić, stosując abstrakcyjne klasy podstawowe, między którymi zmniejszały się koszty takich zmian projektowych, ale były one nadal drogie.
Alternatywnie zacząłem badać architekturę systemu encja-komponent, stosowaną raczej powszechnie w branży gier. To zmieniło wszystko tak:
I wow! To była taka różnica pod względem łatwości konserwacji. Zależności nie płynęły już w kierunku abstrakcji , ale w kierunku danych (komponentów). I przynajmniej w moim przypadku dane były znacznie bardziej stabilne i łatwiejsze do poprawnego zaprojektowania, pomimo zmieniających się wymagań (chociaż to, co możemy zrobić z tymi samymi danymi, ciągle się zmienia wraz ze zmieniającymi się wymaganiami).
Ponieważ jednostki w ECS używają kompozycji zamiast dziedziczenia, tak naprawdę nie muszą zawierać funkcji. Są tylko analogicznym „pojemnikiem komponentów”. Dzięki temu analogiczne 200 podtypów, które implementowały interfejs ruchu , zamieniają się w 200 instancji encji (nie oddzielne typy z osobnym kodem), które po prostu przechowują komponent ruchu (który jest niczym innym jak danymi związanymi z ruchem). A
PointLight
nie jest już osobną klasą / podtypem. To wcale nie jest klasa. Jest to przykład bytu, który po prostu łączy niektóre komponenty (dane) związane z tym, gdzie jest w przestrzeni (ruch) i określone właściwości świateł punktowych. Jedyną związaną z nimi funkcją jest system, taki jakRenderSystem
, który szuka lekkich komponentów w scenie, aby określić sposób renderowania sceny.Wraz ze zmieniającymi się wymaganiami w ramach podejścia ECS często trzeba było zmienić tylko jeden lub dwa systemy działające na tych danych lub po prostu wprowadzić nowy system z boku lub wprowadzić nowy komponent, jeśli potrzebne byłyby nowe dane.
Tak więc przynajmniej dla mojej domeny i jestem prawie pewien, że nie dla wszystkich, to znacznie ułatwiło sprawę, ponieważ zależności płynęły w kierunku stabilności (rzeczy, które wcale nie musiały się często zmieniać). Tak nie było w architekturze COM, gdy zależności jednorodnie płynęły w kierunku abstrakcji. W moim przypadku o wiele łatwiej jest dowiedzieć się, jakie dane są wymagane do ruchu z góry, niż wszystkie możliwe rzeczy, które możesz z tym zrobić, co często zmienia się nieco w ciągu miesięcy lub lat, gdy pojawiają się nowe wymagania.
Cóż, czystego kodu nie mogę powiedzieć, ponieważ niektórzy ludzie utożsamiają czysty kod z SOLID, ale zdecydowanie są pewne przypadki, w których oddzielanie danych od funkcjonalności, podobnie jak ECS, i przekierowywanie zależności od abstrakcji w kierunku danych zdecydowanie może znacznie ułatwić zmień, z oczywistych powodów sprzężenia, jeśli dane będą znacznie bardziej stabilne niż abstrakcje. Oczywiście zależności od danych mogą utrudniać utrzymanie niezmienników, ale ECS ma tendencję do ograniczania tego do minimum dzięki organizacji systemu, która minimalizuje liczbę systemów, które uzyskują dostęp do dowolnego typu elementu.
Zależności nie muszą koniecznie płynąć w kierunku abstrakcji, jak sugerowałby DIP; zależności powinny płynąć w kierunku rzeczy, które prawdopodobnie nie będą wymagały przyszłych zmian. To mogą być lub nie abstrakcje we wszystkich przypadkach (z pewnością nie były moje).
Nie jestem pewien, czy ECS jest naprawdę smakiem OOP. Niektórzy ludzie definiują to w ten sposób, ale widzę, że jest to zupełnie odmienne z natury charakterystyka sprzężenia i oddzielenie danych (komponentów) od funkcjonalności (systemów) i braku enkapsulacji danych. Gdyby uznać to za formę OOP, pomyślałbym, że jest to w dużym stopniu sprzeczne z SOLID (przynajmniej najostrzejsze idee SRP, open / closed, substytucja Liskova i DIP). Mam jednak nadzieję, że jest to rozsądny przykład jednego przypadku i dziedziny, w której najbardziej fundamentalne aspekty SOLID, przynajmniej tak, jak ludzie na ogół interpretują je w bardziej rozpoznawalnym kontekście OOP, mogą nie mieć zastosowania.
Teeny Classes
ECS podważyło i zmieniło moje poglądy. Podobnie jak ty, myślałem, że samą ideą łatwości utrzymania jest najprostsza implementacja rzeczy możliwych, co implikuje wiele rzeczy, a ponadto wiele współzależnych rzeczy (nawet jeśli współzależności występują między abstrakcjami). Jest to najbardziej sensowne, jeśli przybliżasz tylko jedną klasę lub funkcję, aby zobaczyć najprostszą i najprostszą implementację, a jeśli jej nie widzimy, przebuduj ją, a może nawet rozłóż. Ale w rezultacie łatwo może przeoczyć to, co dzieje się ze światem zewnętrznym, ponieważ za każdym razem, gdy dzielisz coś stosunkowo złożonego na 2 lub więcej rzeczy, te 2 lub więcej rzeczy musi nieuchronnie oddziaływać * (patrz poniżej) w niektórych sposób, albo coś na zewnątrz musi wchodzić w interakcję z nimi wszystkimi.
W dzisiejszych czasach uważam, że istnieje równowaga między prostotą czegoś a ilością rzeczy i wymaganą interakcją. Systemy w ECS wydają się być dość mocny z nietrywialnych wdrożeń działać na danych, jak
PhysicsSystem
alboRenderSystem
alboGuiLayoutSystem
. Jednak fakt, że tak skomplikowany produkt potrzebuje tak niewielu, ułatwia cofnięcie się i uzasadnienie ogólnego zachowania całej bazy kodu. Jest w tym coś, co może sugerować, że nie jest złym pomysłem oparcie się na mniejszej liczbie, bardziej masywnych klas (wciąż wykonujących prawdopodobnie osobliwą odpowiedzialność), jeśli oznacza to mniej klas do utrzymania i uzasadnienia oraz mniej interakcji w całym tekście system.Interakcje
Mówię „interakcje”, a nie „łączenie” (choć redukcja interakcji oznacza redukcję obu), ponieważ można użyć abstrakcji, aby rozdzielić dwa konkretne obiekty, ale one nadal ze sobą rozmawiają. Nadal mogą powodować działania niepożądane w procesie tej pośredniej komunikacji. I często uważam, że moja zdolność do rozumowania poprawności systemu jest bardziej związana z tymi „interakcjami” niż z „sprzężeniem”. Minimalizowanie interakcji znacznie ułatwia mi rozumowanie wszystkiego z lotu ptaka. Oznacza to, że rzeczy w ogóle się ze sobą nie rozmawiają, i z tego punktu widzenia ECS ma tendencję do naprawdę minimalizowania „interakcji”, a nie tylko łączenia, do najmniejszego minimum (przynajmniej ja nie
To powiedziawszy, może to być przynajmniej częściowo ja i moje osobiste słabości. Znalazłem największą przeszkodę dla mnie, aby stworzyć systemy o ogromnej skali, i nadal pewnie o nich myślę, nawigować po nich i czuję, że mogę wprowadzić wszelkie potencjalnie pożądane zmiany w dowolnym miejscu w przewidywalny sposób, zarządzanie stanem i zasobami oraz skutki uboczne. To największa przeszkoda, która zaczyna się pojawiać, gdy przechodzę od dziesiątek tysięcy LOC do setek tysięcy LOC do milionów LOC, nawet dla kodu, który sam stworzyłem. Jeśli coś spowolni mnie do czołgania się przede wszystkim, mam wrażenie, że nie mogę już zrozumieć, co się dzieje pod względem stanu aplikacji, danych, skutków ubocznych. To' to nie czas robota, którego wymaga wprowadzenie zmian, które mnie spowalniają, tak samo jak niemożność zrozumienia pełnego wpływu zmiany, jeśli system wykracza poza zdolność rozumowania o tym. A zmniejszenie interakcji było dla mnie najskuteczniejszym sposobem na zwiększenie produktu o wiele więcej funkcji, przy czym osobiście nie jestem przytłoczony tymi rzeczami, ponieważ ograniczenie interakcji do minimum również zmniejsza liczbę miejsc, które mogą może nawet zmienić stan aplikacji i spowodować znaczne skutki uboczne.
Może zmienić coś w ten sposób (gdzie wszystko na diagramie ma funkcjonalność, i oczywiście scenariusz w świecie rzeczywistym miałby wiele, wiele razy więcej obiektów, a jest to diagram „interakcji”, a nie sprzężony, jako sprzężenie pomiędzy nimi byłyby abstrakcje):
... do tego, gdzie funkcjonują tylko systemy (niebieskie komponenty są teraz tylko danymi, a teraz jest to schemat sprzęgania):
Pojawiają się przemyślenia na ten temat i być może sposób na ujęcie niektórych z tych korzyści w bardziej zgodny kontekst OOP, który jest bardziej kompatybilny z SOLID, ale jeszcze nie do końca znalazłem projekty i słowa, i znajduję to trudne, ponieważ terminologia była przyzwyczajona do rzucania wszystkimi związanymi bezpośrednio z OOP. Próbuję to rozgryźć, czytając odpowiedzi ludzi tutaj, a także staram się sformułować własne, ale są pewne rzeczy bardzo interesujące w naturze ECS, których nie byłem w stanie idealnie położyć na tym palca może mieć szersze zastosowanie nawet do architektur, które go nie używają. Mam również nadzieję, że ta odpowiedź nie wyjdzie jako promocja ECS! Uważam to za bardzo interesujące, ponieważ projektowanie ECS naprawdę zmieniło moje myśli drastycznie,
źródło
Zasady SOLID są dobre, ale KISS i YAGNI mają pierwszeństwo w przypadku konfliktów. Celem SOLID jest zarządzanie złożonością, ale jeśli zastosowanie SOLID powoduje, że kod staje się bardziej złożony, pokonuje to cel. Na przykład, jeśli masz mały program z zaledwie kilkoma klasami, zastosowanie DI, segregacja interfejsu itp. Może bardzo zwiększyć ogólną złożoność programu.
Zasada otwarta / zamknięta jest szczególnie kontrowersyjna. Zasadniczo mówi, że należy dodać funkcje do klasy, tworząc podklasę lub oddzielną implementację tego samego interfejsu, ale nie zmieniając oryginalnej klasy. Jeśli utrzymujesz bibliotekę lub usługę używaną przez nieskoordynowanych klientów, ma to sens, ponieważ nie chcesz przerywać zachowania, na którym może polegać wychodzący klient. Jeśli jednak modyfikujesz klasę, która jest używana tylko we własnej aplikacji, modyfikacja klasy jest często znacznie prostsza i czystsza.
źródło
Z mojego doświadczenia:
Duże klasy są wykładniczo trudniejsze do utrzymania, ponieważ stają się większe.
Małe klasy są łatwiejsze w utrzymaniu, a trudność w utrzymaniu zbioru małych klas rośnie arytmetycznie do liczby klas.
Czasami duże klasy są nieuniknione, duży problem czasami wymaga dużej klasy, ale są znacznie trudniejsze do odczytania i zrozumienia. W przypadku dobrze nazwanych mniejszych klas wystarczy sama nazwa do zrozumienia, nie trzeba nawet patrzeć na kod, chyba że istnieje bardzo specyficzny problem z klasą.
źródło
Zawsze trudno mi wytyczyć granicę między „S” bryły a (być może staroświecką) potrzebą, aby obiekt miał całą wiedzę o sobie.
15 lat temu współpracowałem z programistami Deplhi, którzy mieli świętego Graala, aby mieć klasy zawierające wszystko. Tak więc w przypadku kredytu hipotecznego można dodać ubezpieczenia i płatności oraz dochody i pożyczki oraz informacje podatkowe, a także obliczyć podatek do zapłaty, podatek do odliczenia, a może on ulec serializacji, utrzymać się na dysku itp.
A teraz mam ten sam obiekt podzielony na wiele i wiele mniejszych klas, które mają tę zaletę, że można je testować jednostkowo znacznie łatwiej i lepiej, ale nie dają dobrego obrazu całego modelu biznesowego.
I tak, wiem, że nie chcesz wiązać swoich obiektów z bazą danych, ale możesz wstrzyknąć repozytorium w dużej klasie hipotecznej, aby to rozwiązać.
Poza tym nie zawsze łatwo jest znaleźć dobre nazwy dla klas, które robią tylko małe rzeczy.
Nie jestem więc pewien, czy „S” jest zawsze dobrym pomysłem.
źródło