Programowanie

Zasady Solid

zasady solid

Na blogu nuda, aż piszczy. Pora się otrząsnąć i wyjść naprzeciw oczekiwaniom tym kilku osobom, które z utęsknieniem wyczekiwały nowych wpisów na mojej stronie i tak mocno się rozczarowały 😉 Często umieszczam to czego sam aktualnie się uczę. Zauważyłem, że czegoś w moim kodzie brakuje. Po chwili namysłu stwierdziłem, że tym czymś jest swoisty zbiór reguł i zasad, który usystematyzowałby i poprawił mój kod. Taki, który brutalnie wziąłby klasy za fraki. Przedstawiam wam zasady Solid.

Czym są zasady Solid?

Jest to zbiór 5 reguł autorstwa prawdziwego guru „zero-jedynkowego” porządku, kodowego pedanta, autora książki „Czysty kod” – Roberta Martina, których przestrzeganie ma pomagać w utrzymywaniu porządku w kodzie języków obiektowych i w uniknięciu niepotrzebnych problemów w dłuższej perspektywie czasowej. Zasady Solid mają proste założenie: twórz taki kod, aby osoba spoza projektu nie miała problemów z jego zrozumieniem teraz zarówno teraz, jak i za parę miesięcy czy lat. Nie sztuką jest napisać coś co się kompiluje, uruchamia i działa. Kwintesencja umiejętnego pisania kodu jest jego właściwe formatowanie. Program ma nie sprawiać problemów w rozwijaniu i udoskonalaniu go.

Bardzo prawdopodobne, że program pisany z przymrużeniem oka na jakikolwiek z postulatów zasad Solid, prędzej, czy później i tak potrzebować będzie refaktoryzacji. Dlatego piszmy tak, aby możliwie jak najbardziej ograniczyć konieczność poprawy kodu za X miesięcy.

Zasady Solid są wręcz kluczową sprawą w projektach rozwijanych przez firmy. Jeśli jako Junior Developer, reguły wujaszka Boba są Ci obce to polecam jak najszybciej się z nimi zapoznać, a samą książkę „Czysty kod” warto dopisać do swojej listy książek, które koniecznie musisz przeczytać 😉. Nic nie stracisz, a na pewno sporo zyskasz. Sam zacząłem wprowadzać zasady Solid do swoich projektów i liczę na to, że z biegiem czasu hołdowanie tym zasadom stanie się dla mnie tak oczywiste i naturalne jak oddychanie 😊.

Zasady Solid – 5 przykazań schludnego programisty

Skrót SOLID rozwija się następująco:

  • Single responsibility principle – zasada jednej odpowiedzialności,
  • Open/closed principle – zasada otwarte-zamknięte,
  • Liskov substitution principle – zasada podstawienia Liskov,
  • Interface segregation principle – zasada segregacji interfejsów,
  • Dependency inversion principle – zasada odwrócenia zależności.

Co stoi za tymi niejasno brzmiącymi hasłami? O tym poniżej. Krótko, rzeczowo i w przyswajalny sposób.

zasady solid

Single responsibility principle – zasada jednej odpowiedzialności

Pierwsza z zasad SOLID jest lekka, prosta, genialna i przyjemna. Założeniem jest, aby każda z klas miała tylko jeden cel, jedno przeznaczenie i jedną odpowiedzialność. Lepszym rozwiązaniem jest stworzenie kilku mniejszych klas, każda odpowiedzialna za coś innego. Ta praktyka jest szczególnie pomocna w utrzymaniu kodu i jego dalszym rozwoju. Zwiększasz jego czytelność i użyteczność.

Przykłady będą wręcz trywialne. Mają w prosty sposób przekazać istotę zasady SRP. Mamy taką oto klasę napisaną niezgodnie z powyższą zasadą:

Jak można ją zrefaktoryzować? Dobrym pomysłem będzie rozbicie klasy na dwie mniejsze. W ten sposób w późniejszej fazie projektu będzie można skorzystać z możliwości utworzenia obiektu dla klienta, niezwiązanego w żaden sposób z klasą zamówienie. A co z metodą sprawdzającą poprawność kodu pocztowego? Zawrzemy ja w nowoutworzonej klasie klienta? Otóż nie. Zasada jednej odpowiedzialności mówi, że może istnieć tylko jeden powód do zmian w klasie. Gdybyśmy chcieli dodać dodatkową informacje o kliencie i zmienić np. warunek w metodzie sprawdzającą kod pocztowy to mielibyśmy już dwa powody do zmiany klasy, wbrew SRP. Zatem tworzymy oddzielną klasę sprawdzającą tenże kod pocztowy.

Myślałem, że uniknę tego porównania, ale jest zbyt obrazowe, aby go nie użyć. Mianowicie, duża klasa jest jak taki gruby, wielofunkcyjny scyzoryk. Zmierzać mamy do tego, aby jego elementy rozbijać na możliwie jak najmniejsze.

Open/closed principle – zasada otwarte-zamknięte

Zasada ta mówi, że „każda klasa czy funkcja powinna być otwarta na rozszerzenia, ale zamknięta na modyfikacje”. Co to oznacza w praktyce? Od początku mamy pisać w ten sposób, aby możliwe było dodawanie nowych metod w klasie, rozwijanie jej funkcjonalności bez jakichkolwiek późniejszych zmian w systemie. W przypadku rozbudowanych systemów, zawierających sporo klas i funkcji, modyfikacja nawet jednej małej metody może spowodować nieprawidłowe działanie innych elementów programu.

Spójrzmy na następujący przykład:

Za każdym razem, gdy dodać będziemy chcieli wyrok dla nowego rodzaju przestępcy to musimy edytować metodę Judgement klasy sędziego. Co gdy przewidujemy nieustanne dodawanie nowych rodzajów przestępców i wyroków dla nich? Z pomocą przychodzi polimorfizm. Jeden z fundamentów obiektowego podejścia do programowania. Powinienem kiedyś przybliżyć wam z czym to się je, ale tymczasem spójrzcie:

Po refaktoryzacji możemy dodawać niezliczoną ilość nowych klas dla przestępców nie modyfikując przy tym klasy sędziego.

Mi osobiście zasada OCP kojarzy się z pewną regułą, którą stosują matematycy i fizycy -> „Nowe nie może burzyć starego”.

Liskov substitution principle – zasada podstawienia Liskov

Kolejna wdzięczna reguła paradygmatu SOLID. Jej przestrzeganie wymaga rozumnego przemyślenia schematu mechanizmu dziedziczenia w naszym kodzie. Właściwie wymuszając zastosowanie polimorfizmu. Zasada podstawienia Liskov brzmi następująco:

„Funkcje, korzystające z obiektów klasy bazowej, muszą być w stanie używać również obiektów klasy pochodnej, bez dokładnej znajomości tych obiektów.”

Idąc tropem naszych przestępców rozważmy kolejny przykład.

Chcielibyśmy, aby przy wywoływaniu metody klasy pochodnej SerialKiller przysłoniła metodę klasy bazowej Murderer. Zamiast tego funkcja judge() wymusza na nas użycie metody obiektu klasy podanej bazowej podanej jako typ argumentu funkcji.

Co zrobić z tym fantem? Z pomocą przychodzi polimorfizm:

 

Obiekt klasy pochodnej może być od teraz używany przemiennie z obiektem klasy bazowej. Opisywana zasada bardzo dobrze wpisuje się w meandry reguły Open/Closed – „nowe nie może burzyć starego”.

Interfaces segregation principle – zasada segregacji interfejsów

Wiele dedykowanych interfejsów jest lepsze niż jeden ogólny. Krótka piłka.

Żeby zrozumieć jak praktycznie można wykorzystać tą zasadę spójrzmy od razu na przykład, bez owijania w bawełnę. Wiele więcej do zrozumienia ISP nie trzeba.

Mamy interfejs wejścia/wyjścia z dwiema metodami wirtualnymi i dwie klasy dziedziczące po interfejsie z tym, że tylko jeden używa wszystkich funkcji interfejsu. Dążyć należy do sytuacji, w której każda klasa wykorzystuje wszystkie metody otrzymane w spadku. Dlatego rozbijemy klasę Input/Output na dwie mniejsze. W końcu nasza drukarka i tak nie korzysta z opcji skanowania.

 

Dependency inversion principle – zasada odwrócenia zależności

Piąta i ostatnia zasada SOLID formułuje się następująco:

„Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych – zależności między nimi powinny wynikać z abstrakcji.”

DIP zaleca korzystanie z abstrakcyjnych klas w postaci buforu pomiędzy wspomnianymi wysoko i niskopoziomowymi modułami. Sugeruje używać polimorfizmu najczęściej jak to możliwe. Dzięki tej zasadzie kod staje się bardziej elastyczny i mnie „przyszyty” do konkretnej implementacji.

Za przykład posłużą mi klasy użyte przy okazji omawiania zasady otwarte-zamknięte.

Metoda Judgement() klasy Judge nie jest zupełnie zależny od konkretnego typu. Klasa Criminal pełni tutaj rolę interfejsu właśnie. Dzięki niemu sędzia może orzekać wyrok dla każdego z przestępców bez konieczności dostosowania implementacji tejże metody.

 

A komu to potrzebne? A po co?

Początkowo wprowadzenie wszystkich tych pięciu zasad może okazać się kłopotliwe i zbędne. Po czasie wchodzi w krew, ale i tak niekoniecznie widzisz słuszność w tym co robisz. Zasady SOLID docenia się w trzech przypadkach:

  • Wracając po jakimś czasie do kodu,
  • W sytuacji gdy projekt się rozwijany na bieżąco,
  • Przy pracy zespołowej, gdy kod nie jest tylko twój -> jest wspólny i należy o niego dbać lepiej niż o żonę 😉 #wyolbrzymienie

To moje rozumienie zasad Roberta Martina. Dajcie znać co o tym sądzicie. Może udało mi się komuś „rozjaśnić” w głowie, a może coś dało się wytłumaczyć lepiej? Zapraszam do komentowania 😊.

2 Comments on “Zasady Solid

  1. Bardzo fajny artykuł dedykowany dla WSZYSTKICH programistów 🙂 Mam tylko takie pytanko, czy takie rozdrobnienie kodu nie spowoduje, że program będzie „cięższy” i bardziej pamięciożerny ?

    1. To bardzo trafne pytanie 😉 Ogólnie rzecz biorąc to kod rzeczywiście może stracić na wydajności z uwagi na dużo warstw abstrakcji, ale to nie zawsze jest zasada. Na szybkość programu wpływają głównie (to jest jakiś duży procent) tzw. wąskie gardła – źródła wszelkiego zła. Na nich skupiasz się najpierw, potem ew. obwiniasz Solid 😀
      W skrócie SOLID może, ale nie musi obciążać programu.

Dodaj komentarz