Tworzenie kodu według najlepszych praktyk
2 godz. 45 min · Full-stack i Programowanie
Rafał PiekaraSoftware KomandosNie zrozumiesz SOLIDa bez teoretycznych podstaw. Przejdziemy przez założenia stojące za każdą z nich. Przeanalizujemy problemy, które możesz spotkać oraz dowiesz się dlaczego ciągle jeszcze tak wielu programistów nie stosuje się do tych zasad.
Ruby i Typescript to technologie, które świetnie odzwierciedlają uniwersalizm SOLIDa. Te złote zasady działają w językach dynamicznie typowanych i interpretowanych jak Ruby oraz przy statycznym typowaniu na przykładzie Typescript.
Zanim zabierzesz się za programowanie warto poćwiczyć SOLIDny sposób myślenia. Zmierzysz się z zadaniami, w ramach których będziesz projektować kod tak, aby wszystkie zasady SOLID znalazły w nim swoje zastosowanie.
Prawda jest taka, że kod, z którym w większości pracujemy ma swoją historię. Czasami historia ta jest tak bolesna, że klasy i metody są zagmatwane, poplątane i trudne w utrzymaniu. Gdy dotrzesz do tego miejsca będziesz mieć w arsenale wszystkie potrzebne narzędzia, żeby zmierzyć się z zadaniami praktycznymi, które przekształcą kod legacy w SOLIDne arcydzieło.
Tak ważny temat, jak testowanie nie może zostać pominięty. Między innymi po to też stosujemy zasady SOLID, żeby nasz kod praktycznie sam się testował. Napiszesz więc testy, których wzorce możesz wykorzystać w dowolnym projekcie, dowolnej technologii, jeśli tylko kod, który testujesz będzie spełniał wymagania zasad SOLID.
Nie ma znaczenia, czy dopiero zaczynasz przygodę z programowaniem, czy też jesteś starym wyjadaczem, który klasami i interfejsami myśli przy zagryzaniu tosta na śniadanie. Ten kurs jest właśnie dla Ciebie. Technologia ani język programowania też nie mają znaczenia. Zasady SOLID to uniwersalny zbiór. Ich znajomość sprawi, że Twój kod będzie lepszy, łatwiejszy w utrzymaniu i odporny na perturbacje i zmiany.
Sama zasada else brzmi bardzo abstrakcyjnie i może się wydawać trudna i
odstraszająca, ale nie martw się, to nie jest aż takie skomplikowane.
A dlaczego? Przejdźmy teraz do kodu.
Kod odpowie nam najwięcej.
Mamy taką klasę Billing, która po prostu odpowiada za procesowanie
jakiejś płatności.
I ta klasa Billing posiada publiczną metodę run, która przyjmuje
parametr license i ta line.
Sens ma metodę Calculator, czyli oblicza jakieś tam należność
odnośnie używanej licencji i samo to, że ta licencja jest przekazywana jako
parametr może nam sugerować, że mamy w naszym systemie różne rodzaje licencji
i mamy jakąś standardową licencję, w której można obliczyć należność.
Mamy też licencję prywatną Personal License, gdzie też obliczamy
należność i mamy też biznesową licencję, w której zaliczamy należność,
ale już na innych warunkach.
I te trzy klasy licence, personal, licence, business, licence i licence mają
dokładnie ten sam interfejs, tą samą metodę publiczną, kalkulacji.
One mogą dziedziczyć po klasie bazowej Licence, wobec czego możemy je podstawiać
dowolnie w metodzie Run w klasie Billing, obliczając należność do zapłaty.
Licencja w tym kodzie spełnia zasady LSB.
Mamy łańcuch dziedziczenia, który pozwala na swobodne podmiany.
No ale teraz o co chodzi w ogóle z tym kwadratem i prostokątem?
Mamy tutaj przykład napisany w Javie.
Mamy klasę reg Tangle, który jest prostokąt i mamy klasę kwadratu, który
dziedziczy po prostokącie zgodnie z zasadą każdy kwadrat jest prostokątem.
Wobec czego możemy zbudować sobie taki kwadrat posługując się metodami setów
setów, set have i licząc jego pole. I co się tutaj dzieje?
Ponieważ każdy kwadrat jest prostokątem.
Stosujemy metodę liczenia pola dla prostokąta A i razy B, czyli wysokość razy
szerokość i obliczając pole dla kwadratu, który będzie miał
wysokość i szerokość 5 i 2 waga.
Pułapka logiczna będzie nam ta metoda zwracała wartość 10.
A to jest nieprawda, bo dla kwadratu wysokość i szerokość jest taka sama.
Czyli jeśli najpierw ustalimy wysokość na 5, a potem ustawimy ją na 2,
to w konsekwencji dostaniemy bok o długości 2, więc wynik
10 będzie tutaj przekłamaniem?
Idąc dalej możemy zdefiniować taką klasę jak Tangle i zdefiniować jej parametry.
Mamy tutaj dwa parametry wysokość, szerokość, liczenie pola
prostokąta, wysokość raty, szerokość.
Wszystko się zgadza.
Natomiast jeśli mamy kwadrat.
Tutaj już ta logika w kwadracie będzie trochę bardziej złożona,
ponieważ w momencie kiedy będziemy przypisywać do kwadratu wysokość
lub wartość długości boku, to okaże się, że musimy ustalić
tę wartość dla każdego z boków.
Więc będziemy mieć tutaj metodę set size, bo kwadrat nie będzie miał wysokości
szerokości, będzie miał po prostu długość boku i wtedy napisana metoda dla liczenia
pola będzie potęgowała nam wartość tej długości boku.
W ten oto sposób nie będziemy mieć przejrzystego łańcucha
dziedziczenia, bo musimy ten kwadrat zmodyfikować, mimo że on teoretycznie
spełnia tę zasadę.
Spełnia zasadę, że każdy kwadrat jest prostokątem, to jednak w naszym przypadku
nie będzie działał tak jak należy.
A to właśnie przez to, że będziemy musieli zmodyfikować samo ciało klasy kwadratu,
żeby nam odpowiednio wyliczyć pole.
Rozwiązaniem z tego może być np.
skasowanie typu reg tangle na kwadrat.
W językach, które takie kasowanie umożliwiają i wtedy przypisanie
wartości boków odbędzie się prawidłowo.
Więc tutaj mamy kasowanie tej klasy reg Tangle na kwadrat i będzie
mieć przypisanie szerokości i wysokości, więc ta ostatnia
wartość będzie będzie prawidłowa.
Będzie tutaj Square Aria to się równa 10, bo nasz kwadrat jest w tym momencie
prostokątem do Do stworzenia instancji kwadratu użyliśmy klasy prostokąta.
Tak na logikę nie ma sensu.
Do końca natomiast programistyczne spełniamy po prostu
wzorce i zasadę LSB in.
Innym przykładem może być np. fakturowanie.
Mamy tutaj jakąś klasę canvas, która będzie generowała
jakąś fakturę o określonej wartości.
Coś tam zwraca ta metoda Generate Canvas.
invalid zwraca określoną wartość.
Będziemy mogli mieć prywatną fakturę czy jakieś private voice wystawione
dla mnie i będę miał zniżkę tam 5%.
I zobacz, że ta klasa dziedziczy po tej klasie bazowej n Voice i definiuje
dokładnie ten sam interfejs Generate Info.
To jest dokładnie ten sam interfejs.
Nic nowego się tutaj nie zmieniło, natomiast dolicza się ta zniżka.
Kolejna klasa to będzie Business in Voice, gdzie do faktury potrzebujemy NIP i ten
interfejs jest znów zdefiniowany, więc mamy spójny łańcuch dziedziczenia.
I już widać na pierwszy rzut oka, że te klasy faktury będą mogły być używane
naprzemiennie w zależności od warunków biznesowych, bez konieczności robienia
jakiejś dodatkowej ideologii i innych tego typu rzeczy.
Wystarczy nam sama fabryka, która to wytworzy i możemy drukować sobie faktury.
I zobacz, że drukarka naszych faktur przyjmuje po prostu
prosty parametr canvas i samo to, że możemy przekazać jakąś fakturę już nam
może sugerować, że będziemy korzystać z różnych klas faktur z
różnych instancji i przez to, że nasza metoda print in voice będzie
działała wszędzie tam metoda generate in voice.
Na tej fakturze, bo mamy spójny interfejs, będziemy mogli skończyć z kodem, który
będzie wyglądał w ten sposób, gdzie mamy klasę drukarki Printer A i możemy
wydrukować sobie prywatną fakturę i biznesową fakturę.
A to jaka to jest faktura to już drukarki nie obchodzi, bo ona sobie ją wygeneruje.
Dzięki właśnie temu wspólnemu interfejsowi.
Dodatkowo mamy jeszcze wstrzykiwanie zależności, więc tutaj mamy wspólny
interfejs, spójny łańcuch dziedziczenia, przez co spełniamy zasadę LSB,
zasadę przedstawień Barbary List of.