2.04.20206 min

Krzysztof MasłykExpert Java Enterprise Designer

Programowanie SOLID w praktyce

Poznaj SOLID, czyli podejście, do tworzenia programów oparte na popularnych zasadach projektowania, które pomogą Ci uniknąć podstawowych błędów podczas tworzenia aplikacji komputerowych.

Programowanie SOLID w praktyce

W dobie otaczającej nas rewolucji cyfrowej technologia oprogramowania rozwija się bardzo dynamicznie, udostępniając co chwilę nowe standardy komunikacji i nowe narzędzia do budowy ekosystemów IT takie jak biblioteki programistyczne czy całe frameworki. Giganci z branży zmienili podejście do wersjonowania swoich produktów wypuszczając kwartalnie ich kolejne wersje. Wcześniej było to wykonywane z częstotliwością raz na kilkanaście lub więcej miesięcy.

Jako developerzy skupiamy się na poznawaniu nowych technologii implementujących często te same funkcjonalności biznesowe wierząc w to, że modna biblioteka czy trendy framework rozwiąże za nas trudne problemy technologiczne albo uchroni przed złożonością domeny. Tak nie jest, a skutki takiego myślenia możemy czasami zaobserwować w systemach, które tworzymy.

Na szczęście istnieją aspekty developmentu, które nie straciły na aktualności od momentu ich opublikowania. Są to tzw. zasady projektowania oraz wzorce projektowe. Występują w wielu dziedzinach życia oprócz IT (np. w budownictwie) i są dużej mierze niezależne od technologii.

W niniejszym artykule przedstawię podejście, do tworzenia programów oparte na popularnych zasadach projektowania znanych pod dobrze brzmiącą nazwą SOLID.

Zasady SOLID stanowią zbiór wytycznych, które pomagają nam uniknąć podstawowych błędów podczas tworzenia aplikacji komputerowych. Nazwa SOLID to akronim od pierwszych liter najważniejszych zasad. Zostały one opracowane przez Roberta C. Martina, który opublikował je w manifeście „Design Principles and Design Patterns” oraz w swojej książce pt. „Agile Software Development: Principles, Patterns and Practice”.

Według Roberta Martina istnieją 3 najważniejsze cechy złego projektu, których należy unikać:

  • Sztywność – trudno to zmienić, ponieważ każda zmiana wpływa na zbyt wiele innych części systemu,
  • Kruchość – po wprowadzeniu zmiany nieoczekiwane części systemu ulegają awarii,
  • Nieprzenośność – ponowne użycie w innej aplikacji jest trudne, ponieważ nie można jej rozdzielić od aktualnych zależności.


Wyżej wymienione wady oprogramowania można zminimalizować, jeśli sposób rozwijania tego oprogramowania będzie zgodny z niżej wymienionymi pięcioma zasadami. Reguły te zaprezentuję i opiszę poniżej stosując przykłady w popularnym języku obiektowym.


Single Responsibility Principle

A class should have only one reason to change.

Klasa powinna mieć tylko jeden powód do zmiany. W tym kontekście odpowiedzialność uważa się za jeden z powodów zmiany. Zasada ta mówi, że jeśli mamy 2 powody, by zmienić klasę, musimy podzielić funkcjonalność na dwie klasy. Każda klasa poradzi sobie tylko z jedną odpowiedzialnością, a w przyszłości, jeśli będziemy musieli dokonać jednej zmiany, zrobimy to w klasie, która ją obsługuje. Kiedy musimy wprowadzić zmiany w klasie, która ma więcej obowiązków, zmiana może wpłynąć na kod, który nie powinien być modyfikowany.

Czas na przykład złamania zasady SRP:

Kod programu po korekcie:

W kodzie po zmianie zmodyfikowano sygnaturę metody setContent() z parametrem IContent po to, aby obiekt Email nie musiał być modyfikowany za każdym razem, gdy wymaganie biznesowe wprowadzi nowy format do korespondencji np. HTML, etc. Drugi powód do zmiany klasy Email to protokół komunikacji np. POP3, IMAP, etc.


Open Close Principle

Software entities like classes, modules and functions should be open for extension but closed for modifications.

Elementy oprogramowania, takie jak klasy, moduły i funkcje, powinny być otwarte na rozszerzenia, ale zamknięte dla modyfikacji. Zasada ta jest stosunkowo intuicyjna, bo polega na tym by tak zaprojektować rozwiązanie, aby implementacja nowych funkcjonalności nie zmieniała istniejącego kodu a jedynie go rozszerzała. Najczęstsze sposoby na zastosowanie tej zasady to tzw. polimorfizm, wzorzec strategii i odwrócenie zależności.

Przykład:

W poniższym przykładzie nie spełniającym zasady OCP widać, że klasa Drawer musi zostać zmodyfikowana za każdym razem gdy pojawi się wymaganie rysowania nowego rodzaju kształtu.

Po zastosowaniu zasady OCP klasa Drawer nie jest zmieniana, gdy dochodzi nowy kształt:


Liskov's Substitution Principle

Derived types must be completely substitutable for their base types.

Typy pochodne muszą całkowicie zastępować typy podstawowe. Zasada ta jest rozszerzeniem zasady Open Close Principle pod względem zachowania, co oznacza, że nowe klasy pochodne rozszerzają klasy podstawowe bez zmiany ich zachowania. Innymi słowy klasy pochodne powinny móc zastąpić klasy bazowe bez żadnych zmian w kodzie. Zasadę tę Robert Martin zaczerpną od Barbary Liskov (profesor MIT). Została ona opublikowana w 1987r.

Przykład:

Opisuje zastąpienie klasy bazowej Rectangle jej rozszerzeniem w postaci klasy Square.

Powyższy przykład pokazuje, że zasada zastępowalności LSP nie została utrzymana, ponieważ wynik funkcji getArea() nie działa tak samo, gdy zastąpimy klasę pochodną klasą bazową.  


Interface Segregation Principle

Clients should not be forced to depend upon interfaces that they don't use.

Klienci nie powinni być zmuszani do polegania na interfejsach, których nie używają. Ta zasada uczy nas dbać o to, jak piszemy nasze interfejsy. Pisząc je, powinniśmy zadbać o to, aby dodać tylko metody, które powinny tam być. W przeciwnym wypadku, klasy implementujące interfejs również będą musiały zaimplementować te metody. Na przykład, jeśli utworzymy interfejs o nazwie Worker i dodamy metodę przerwy na posiłek, to wszyscy pracownicy będą musieli ją zaimplementować.

A co, jeśli pracownik jest robotem?

Przykład:

Implementacja niespełniająca zasady ISP. Obiekt Manager otrzymuje kompetencje i teoretyczną możliwość wywołania metody eat() obiektu Worker. Błąd!

Przykład kodu po korekcie spełniającego ISP:

Obiekt Manager, który ma zależność do obiektu pracownika IWorkable otrzymuje dostęp tylko do tych funkcji, którymi jest zainteresowana.


Dependency Inversion Principle

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Abstractions should not depend on details. Details should depend on abstractions.

Moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu. Oba powinny zależeć od abstrakcji. Abstrakcje nie powinny zależeć od szczegółów. Szczegóły powinny zależeć od abstrakcji. Zasada odwrócenia zależności określa, że powinniśmy oddzielać moduły wysokiego poziomu od modułów niskiego poziomu, wprowadzając warstwę abstrakcji między klasami wysokiego poziomu a klasami warstwy niższej. W klasyczny sposób, gdy moduł oprogramowania (klasa, komponent) potrzebuje jakiegoś innego modułu, inicjuje się i zawiera bezpośrednie odniesienie do niego. Spowoduje to ścisłe połączenie tych dwóch elementów. Stosując odwrócenie zależności poprzez warstwę abstrakcji, moduły mogą być łatwo zamieniane na inne.

Jest to w mojej opinii najważniejsza zasada, dotycząca nie tyle projektu systemu co jego architektury.

Przykład kodu łamiącego zasadę DIP:

Widać, że obiekt Manager jest bezpośrednio zależny od obiektu Worker, co powoduje, że te dwa elementy są ze sobą trwale powiązane i zmiana Worker na alternatywny nie może zostać wykonana bez modyfikacji klasy Manager.

Kod spełniający zasadę DIP

Po korekcie obiekt Manager został pozbawiony sztywnej zależności do obiektu Worker, co daje nam możliwość podmiany implementacji interfejsu IWorker w czasie rzeczywistym bez modyfikowania warstwy wyższego rzędu.

Zasada odwrócenia zależności wspiera również zasadę Open Close Principle, ponieważ wersja programu po korekcie może być rozszerzana bez konieczności modyfikowania istniejących klas i modułów.

Bardzo dobrym miejscem do zastosowania zasady DIP jest styk warstwy aplikacji z warstwą dostępu do danych (DAO). Warstwa aplikacji powinna być zależna jedynie od zbioru interfejsów (warstwa abstrakcji), których używa w sposób ślepy „nie wiedząc” jaka technologia DAO jest aktualnie w użyciu. Taka struktura aplikacji umożliwia wymianę technologii DAO oraz DBMS’u w sposób niewidoczny dla reszty architektury systemu.


Podsumowanie i wnioski

Jak już pewnie zauważyliście, nie wszystkie wymienione zasady mają jasno zaznaczone granice. W rezultacie czego, nie możemy stwierdzić z całą pewnością, że jakaś implementacja jest w pełni zgodna bądź w całości niezgodna z danym pryncypium. Jest to pewne kontinuum, a developer powinien dążyć, aby zgodność z zasadami SOLID była możliwie jak najwyższa. Dotyczy to chociażby zasady Single Responsibility Principle, gdzie liczba powodów do zmodyfikowania klasy może być kwestią subiektywną. Ze doświadczenia wiem, że ćwiczenie jest najlepszą drogą do opanowania tej sztuki. Jednak najważniejszą zasadą, która powinna być utrzymana podczas budowy każdego systemu to wg mnie zasada zdrowego rozsądku.

<p>Loading...</p>

Powiązane artykuły

Dziel się wiedzą ze 160 tysiącami naszych czytelników

Zostań autorem Readme

Ubezpieczeniowy Fundusz Gwarancyjny

Specjalista ds. testów

medium

Znamy widełki

Kontrakt B2BUmowa o pracę

Warsaw

Ważna do 27.02.2022

Dobrze
JIRASoapUIOracle Database
Początkujący
FitNesseJMeter

Sii Polska

DevOps Engineer

medium

17 000 - 24 000 PLN

Kontrakt B2B

Warsaw

Ważna do 27.02.2022

Dobrze
AWSGCPGitHub
Bardzo dobrze
Linux

Accenture Polska

Agile Project Manager

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warsaw

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
Agile Methodologies (Scrum, Kanban, Kaizen)
Dobrze
Team-building & people management
Początkujący
cloud fundamentals

DB Schenker Technology Center Warsaw

Business Analyst

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warsaw

Ważna do 27.02.2022

Accenture Polska

PHP/Magento Developer

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
PHPGIT
Dobrze
Magento 2SOAP/REST HTML/CSS
Początkujący
JSONJavaScript jQuery

T-Mobile Polska S. A.

Inżynier Sieciowy

medium

Brak widełek

Kontrakt B2B

Warsaw

Ważna do 27.02.2022

Asseco Poland S.A.

Młodszy Analityk Finansowy

junior

Brak widełek

Umowa o pracę

Rzeszów

Ważna do 27.02.2022

Netguru

Junior iOS Developer

junior

4 200 - 6 000 PLN

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
iOS

7N

Data Engineer

medium

15 100 - 18 100 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
MS SQLETLSpark

7N

Senior Business Analyst

senior

16 800 - 21 800 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022

Więcej od Asseco Poland S.A.

Zobacz wszystkie artykuły od Asseco Poland S.A.