Czytałem tę wiki na temat Stable Abstractions Principle (SAP) .
SAP stwierdza, że im bardziej stabilny pakiet, tym bardziej powinien on być abstrakcyjny. Oznacza to, że jeśli pakiet jest mniej stabilny (bardziej prawdopodobne, że się zmieni), powinien być bardziej konkretny. Nie do końca rozumiem, dlaczego tak powinno być. Z pewnością we wszystkich przypadkach, niezależnie od stabilności, powinniśmy polegać na abstrakcjach i ukrywać konkretne wdrożenie?
design
design-patterns
architecture
object-oriented-design
design-principles
SteveCallender
źródło
źródło
Odpowiedzi:
Myśl o swoich pakietów jak API, aby wziąć przykład z papieru, wziąć do definicji
Reader
zstring Reader.Read()
iWriter
zevoid Writer.Write(string)
jako abstrakcyjnej API.Następnie możesz utworzyć klasę
Copy
za pomocą metodyCopier.Copy(Reader, Writer)
i implementacji,Writer.Write(Reader.Read())
a może nawet kontroli poprawności.Teraz konkretne implementacje, na przykład
FileReader
,FileWriter
,KeyboardReader
iDownloadThingsFromTheInternetReader
.Co jeśli chcesz zmienić swoją implementację
FileReader
? Nie ma problemu, wystarczy zmienić klasę i ponownie skompilować.Co jeśli chcesz zmienić definicję swojej abstrakcji
Reader
? Ups, nie można po prostu zmienić, ale trzeba będzie także zmienićCopier
,FileReader
,KeyboardReader
iDownloadThingsFromTheInternetReader
.Jest to uzasadnienie zasady stabilnej abstrakcji: Uczyń konkretyzację mniej stabilną niż abstrakcje.
źródło
Z powodu YAGNI .
Jeśli obecnie masz tylko jedną implementację jednej rzeczy , po co zawracać sobie głowę dodatkową i bezużyteczną warstwą? Doprowadzi to tylko do niepotrzebnej złożoności. Co gorsza, czasami dostarczasz abstrakcyjne myślenie do dnia, w którym nadejdzie druga implementacja ... i ten dzień nigdy się nie wydarzy. Co za strata pracy!
Myślę też, że prawdziwym pytaniem nie jest: „Czy muszę polegać na abstrakcjach?” ale raczej „Czy potrzebuję modułowości?”. Modułowość nie zawsze jest potrzebna, patrz poniżej.
W firmie, w której pracuję, niektóre oprogramowanie, które rozwijam, jest ściśle powiązane z jakimś sprzętem, z którym musi się komunikować. Urządzenia te zostały opracowane, aby spełniać bardzo specyficzne cele i są praktycznie modułowe. :-) Po pierwsze wyprodukowane urządzenie wychodzi z fabryki i jest zainstalowany gdzieś, zarówno jego oprogramowania i sprzętu może nigdy się nie zmieniają, kiedykolwiek .
Mogę więc mieć pewność, że niektóre części oprogramowania nigdy się nie rozwiną. Te części nie muszą zależeć od abstrakcji, ponieważ istnieje tylko jedna implementacja i ta nigdy się nie zmieni. Deklarowanie abstrakcji w tych częściach kodu tylko dezorientuje wszystkich i zajmuje więcej czasu (bez generowania żadnej wartości).
źródło
Myślę, że być może myli cię słowo stabilne wybrane przez Roberta Martina. Oto, jak myślę, zaczyna się zamieszanie:
Jeśli przeczytasz oryginalny artykuł , zobaczysz (moje wyróżnienie):
Zawsze zmagałem się z wyborem autora słowa „ stabilny” , ponieważ (podobnie jak ty) myślę o aspekcie „prawdopodobieństwa” stabilności, tzn. Raczej nie zmieni się . Trudność oznacza, że zmiana tego modułu spowoduje uszkodzenie wielu innych modułów i naprawienie kodu będzie wymagało dużo pracy.
Martin używa także słów niezależnych i odpowiedzialnych , które mają dla mnie znacznie większe znaczenie. W swoim seminarium szkoleniowym użył metafory o rodzicach dzieci dorastających i o tym, jak powinni być „odpowiedzialni”, ponieważ ich dzieci są od nich zależne. Rozwód, bezrobocie, uwięzienia itp. Są świetnymi przykładami negatywnego wpływu, jaki zmiana rodziców będzie miała na dzieci. Dlatego rodzice powinni być „stabilni” z korzyścią dla swoich dzieci. Nawiasem mówiąc, ta metafora dzieci / rodziców niekoniecznie wiąże się z dziedziczeniem w OOP!
Tak więc, kierując się duchem „odpowiedzialnego”, wymyśliłem alternatywne znaczenie trudnych do zmiany (lub nie powinno się zmieniać ):
Więc podłączenie tych definicji do instrukcji
Przytoczmy zasadę stabilnych abstrakcji (SAP), podkreślając mylące słowa stabilne / niestabilne:
Wyjaśniając to bez tych mylących słów:
TL; DR
Tytuł twojego pytania brzmi:
Myślę, że jeśli poprawnie utworzysz abstrakcje (np. Istnieją, ponieważ zależy od nich wiele kodu), nie będzie żadnych znaczących wad.
źródło
Abstrakcje to rzeczy, które trudno zmienić w oprogramowaniu, ponieważ wszystko zależy od nich. Jeśli twój pakiet będzie się często zmieniał i zawiera abstrakcje, ludzie, którzy na nim polegają, będą zmuszeni przepisać dużą część swojego kodu, gdy coś zmienisz. Ale jeśli twój niestabilny pakiet zawiera konkretne implementacje, po zmianach trzeba będzie napisać znacznie mniej kodu.
Jeśli więc twoja paczka często się zmienia, powinna lepiej zapewniać beton, a nie abstrakcje. W przeciwnym razie ... kto do diabła go wykorzysta? ;)
źródło
Pamiętaj o metodzie stabilności Martina i o tym, co rozumie przez „stabilność”:
Lub:
Oznacza to, że pakiet jest uważany za całkowicie niestabilny, jeśli wszystkie jego zależności są wychodzące: używa innych rzeczy, ale nic go nie używa. W takim przypadku sensowne jest, aby ta rzecz była konkretna. Będzie to również najłatwiejszy rodzaj kodu do zmiany, ponieważ nic innego go nie używa, a zatem nic innego nie może się zepsuć, jeśli ten kod zostanie zmodyfikowany.
W międzyczasie, gdy masz odwrotny scenariusz pełnej „stabilności” z pakietem używanym przez jedną lub więcej rzeczy, ale nie używa on niczego samodzielnie, jak pakiet centralny używany przez oprogramowanie, wtedy Martin mówi, że to powinno być abstrakcyjny. Potwierdza to również część DIP SOLI (D), Zasada Inwersji Zależności, która zasadniczo stwierdza, że zależności powinny jednolicie płynąć w kierunku abstrakcji zarówno dla kodu niskiego, jak i wysokiego poziomu.
Oznacza to, że zależności powinny równomiernie płynąć w kierunku „stabilności”, a dokładniej, zależności powinny płynąć w kierunku pakietów z większą liczbą zależności przychodzących niż zależności wychodzące, a ponadto zależności powinny płynąć w kierunku abstrakcji. Istotą tego uzasadnienia jest to, że abstrakcje zapewniają oddech, aby zastąpić jeden podtyp innym, oferując taki stopień elastyczności, aby konkretne części wdrażające interfejs mogły się zmieniać bez przerywania nadchodzących zależności od tego abstrakcyjnego interfejsu.
Cóż, właściwie nie zgadzam się z Martinem tutaj przynajmniej dla mojej domeny i tutaj muszę wprowadzić nową definicję „stabilności” jak w „braku powodów do zmiany”. W takim przypadku powiedziałbym, że zależności powinny płynąć w kierunku stabilności, ale abstrakcyjne interfejsy nie pomagają, jeśli abstrakcyjne interfejsy są niestabilne (z mojej definicji „niestabilny”, ponieważ podatny na wielokrotne zmiany, a nie Martina). Jeśli programiści nie mogą uzyskać poprawnych abstrakcji, a klienci wielokrotnie zmieniają zdanie w taki sposób, że abstrakcyjne próby modelowania oprogramowania są niekompletne lub nieskuteczne, nie czerpiemy już korzyści ze zwiększonej elastyczności abstrakcyjnych interfejsów w celu ochrony systemu przed kaskadowymi zmianami przerywającymi zależności . W moim osobistym przypadku znalazłem silniki ECS, takie jak te w grach AAA,najbardziej konkretny : w kierunku surowych danych, ale takie dane są wysoce stabilne (jak w „mało prawdopodobne, by kiedykolwiek trzeba je zmienić”). Często zdarza mi się, że prawdopodobieństwo czegoś, co wymaga przyszłych zmian, jest bardziej użyteczną miarą niż stosunek efektorowego do łącznego sprzężenia przy podejmowaniu decyzji dotyczących SE.
Chciałbym więc trochę zmienić DIP i powiedzieć: „zależności powinny przepływać w kierunku komponentów, które mają najniższe prawdopodobieństwo wymagania dalszych zmian”, niezależnie od tego, czy są to abstrakcyjne interfejsy czy surowe dane. Liczy się dla mnie tylko prawdopodobieństwo, że mogą wymagać bezpośrednich zmian przełomowych. Abstrakcje są użyteczne w tym kontekście stabilności tylko wtedy, gdy coś, będąc abstrakcją, zmniejsza to prawdopodobieństwo.
W wielu kontekstach może tak być w przypadku porządnych inżynierów i klientów, którzy z góry przewidują potrzeby oprogramowania i projektują stabilne (jak w niezmienionych) abstrakcjach, podczas gdy abstrakcje te zapewniają im całą swobodę, której potrzebują, aby zamienić konkretne wdrożenia. Ale w niektórych domenach abstrakcje mogą być niestabilne i podatne na nieodpowiednie, podczas gdy dane wymagane od silnika mogą być znacznie łatwiejsze do przewidzenia i stabilne z góry. Tak więc w takich przypadkach może być bardziej korzystne z punktu widzenia łatwości konserwacji (łatwość zmiany i rozszerzenia systemu), aby zależności płynęły w kierunku danych, a nie abstrakcji. W ECS najbardziej niestabilnymi częściami (jak w częściach najczęściej wymienianych) są zazwyczaj funkcje rezydujące w systemach (
PhysicsSystem
, np.), podczas gdy najbardziej stabilne części (jak najmniej prawdopodobne, że zostaną zmienione) to komponenty, które składają się z surowych danych (MotionComponent
np.), z których korzystają wszystkie systemy.źródło