Dobre praktyki programowania oparte na SOLID, KISS i własnym doświadczeniu

8 minuta
Best Practices in Programming Based on SOLID, KISS and Personal Experience

Wytyczne, których programista powinien przestrzegać na co dzień, są tak samo ważne jak w każdej dziedzinie. Stosowanie tych zasad może z początku wydawać się żmudne i czasochłonne, ale z czasem korzyści stają się widoczne. Skróty, które mogą oszczędzić czas, bywają też źródłem różnych problemów w późniejszych etapach projektu.

Doświadczenie pokazuje, że zaśmiecony kod powoduje problemy, gdy tylko zaczyna się go rozbudowywać. Właśnie dlatego Robert Martin (znany jako Uncle Bob) zebrał zestaw reguł pisania dobrego kodu, które pozwalają tworzyć proste, łatwe w modyfikacji oprogramowanie. Uzupełnieniem tego podejścia jest podstawowa zasada KISS, której autorem jest Kelly Johnson.

W skrócie: KISS („keep it simple, stupid”) oznacza wybór najprostszego projektu, który rozwiązuje problem. SOLID to zestaw pięciu zasad projektowania obiektowego: jedna odpowiedzialność (Single responsibility), otwarte-zamknięte (Open/closed), podstawienie Liskov (Liskov substitution), segregacja interfejsów (Interface segregation) i odwrócenie zależności (Dependency inversion). Razem utrzymują kod czytelny, łatwy do rozbudowy i testowania.

Czym jest KISS?

KISS to termin stworzony w latach 60. XX wieku, który miał odzwierciedlać podejście do tworzenia prostych, trwałych samolotów, które można łatwo naprawić w sytuacji awaryjnej. Pomysł został od tamtej pory przyjęty w wielu dziedzinach nauki i inżynierii.

KISS jest też używany przy rozwiązywaniu problemów programistycznych. W dosłownym tłumaczeniu oznacza „keep it simple, stupid” lub „keep it stupid simple”. Przyjmuje się jednak, że sformułowanie nie miało brzmieć negatywnie, lecz sugerować, że prosty projekt sprawia, że serwis i utrzymanie są dziecinnie łatwe.

Zastosowanie KISS w programowaniu można rozpatrywać w trzech aspektach:

  1. upraszczanie kodu dla bieżącego i przyszłego wspólnego rozwoju. Z mojej perspektywy to nic innego jak umiejętne stosowanie zasad SOLID
  2. pisanie tylko tyle kodu, ile potrzeba do przejścia testu jednostkowego, co wiąże się z TDD i pierwszą zasadą SOLID, czyli jedną odpowiedzialnością
  3. intuicyjne podejście do pisania oprogramowania, przystępne i łatwe do zrozumienia dla każdego

Samo KISS nie ustala sztywnych reguł implementacji kodu, podobnie jak większość zasad SOLID, które są ogólnymi wytycznymi tworzenia kodu zgodnego z SOLID.

Z czego składa się SOLID?

Pięć zasad SOLID zebrał i spopularyzował, jak już wspomniano, Robert Martin. Sam akronim pochodzi od Michaela Feathersa, a poszczególne zasady opierają się na wcześniejszych pracach, takich jak zasada otwarte-zamknięte Bertranda Meyera i zasada podstawienia Barbary Liskov.

  • Single responsibility (jedna odpowiedzialność)
  • Open / Closed (otwarte-zamknięte)
  • Liskov substitution (podstawienie Liskov)
  • Interface segregation (segregacja interfejsów)
  • Dependency inversion (odwrócenie zależności)
ZasadaW jednym zdaniuTypowy sygnał naruszenia
Jedna odpowiedzialnośćKlasa lub metoda ma jeden powód do zmianyKlasa, która waliduje dane, zapisuje je i wysyła e-maile
Otwarte-zamknięteNowe zachowanie dodajesz przez rozszerzenie, a nie edycję przetestowanego koduKażdy nowy przypadek to kolejny if w istniejącej metodzie
Podstawienie LiskovKlasa pochodna może zastąpić bazową bez psucia zachowaniaPodklasa, która dla odziedziczonej metody zgłasza „nieobsługiwane”
Segregacja interfejsówWiele małych interfejsów dla klientów jest lepsze niż jeden dużyKlasy zmuszone do implementacji metod, których nie używają
Odwrócenie zależnościZależ od abstrakcji, a nie od konkretnych implementacjiKlasy, które same tworzą swoje zależności przez new

Jedna odpowiedzialność mówi sama za siebie. Każda klasa lub metoda powinna odpowiadać za jedną, osobną funkcjonalność. Przykład złamania tej zasady widać w poniższym kodzie:

Przykład kodu klasy Person, która dodatkowo waliduje adresy e-mail i kody pocztowe oraz dodaje produkty do koszyka, łamiąc zasadę jednej odpowiedzialności

W powyższym przykładzie zasada jest łamana, bo klasa Person „przekracza swoje kompetencje”. Jej jedyną odpowiedzialnością powinno być reprezentowanie obiektu osoby i jej właściwości, takich jak: imię, wiek, adres (najlepiej też jako osobny obiekt).

Zdecydowanie nie powinna walidować adresu e-mail, kodu pocztowego ani dodawać produktu do koszyka.

Poprawienie powyższych błędów i dostosowanie kodu do zasady jednej odpowiedzialności wygląda następująco:

Kod po refaktoryzacji zgodny z zasadą jednej odpowiedzialności

Stosowanie zasady KISS jest ściśle związane z podejściem TDD. Aby test jednostkowy był poprawny, powinna mu odpowiadać jedna, pojedyncza odpowiedzialność.

Otwarte-zamknięte, czyli otwarte na rozszerzenia, zamknięte na modyfikacje. Zgodnie z tą zasadą klasa lub metoda powinna być napisana tak, aby nowe zachowanie dodawać przez rozszerzanie, bez zmiany kodu, który już działa i jest przetestowany.

Co to oznacza w praktyce: przykłady

Zacznijmy od złamanej zasady poniżej:

Przykład kodu klasy Calculator, której metodę area trzeba modyfikować przy nowych kształtach, łamiąc zasadę otwarte-zamknięte

Dlaczego powyższy kod łamie zasadę otwarte-zamknięte? Bo dodanie kodu do obliczania pola, np. trójkąta, wymagałoby bezpośredniej modyfikacji metody area z klasy Calculator.

Jak więc poprawnie zaimplementować obliczanie pola figury? Przykład poniżej:

Przykład kodu z klasą bazową Shape i osobną klasą pochodną dla każdego kształtu, która definiuje własne obliczanie pola, zgodny z zasadą otwarte-zamknięte

Jak widać, aby dodać figurę z możliwością obliczania jej pola, wystarczy utworzyć kolejną klasę dziedziczącą po klasie Shape i zdefiniować w niej metodę obliczania pola.

Te dwie pierwsze zasady łatwo zastosować w każdej funkcjonalności, nawet małej. Stosując je, możemy pisać kod, który będzie czytelny i zrozumiały dla innych programistów oraz łatwy do rozbudowy.

Na tym etapie zastosowaliśmy dwie pierwsze zasady SOLID: jedną odpowiedzialność i otwarte-zamknięte.

Kolejne trzy zasady stosuje się w bardziej złożonych rozwiązaniach, czyli w rozbudowanej logice.

Trzecia zasada to podstawienie Liskov, która mówi, że obiekty klasy pochodnej muszą dać się używać wszędzie tam, gdzie oczekiwana jest klasa bazowa lub interfejs, bez psucia zachowania programu. Innymi słowy, interfejs, klasa abstrakcyjna i wszystkie metody klasy pochodnej muszą pozostać zgodne.

Co to oznacza w praktyce? Podobnie jak przy poprzednich zasadach pokażemy to na dwóch przykładach: błędnym i poprawnym.

Zwykle najpierw pokazuję wariant niepoprawny:

Przykład kodu z interfejsem Animal oraz klasami Dog i Fish, w którym Fish musi implementować metodę run, łamiąc zasadę podstawienia Liskov

Na pierwszy rzut oka wszystko jest w porządku: jest klasa Animal reprezentująca zwierzę oraz dziedziczące po niej Dog i Fish. Gdzie więc problem? W metodzie wymuszonej przez interfejs pies biega i ma to sens, ale nikt nigdy nie słyszał o biegającej rybie.

Zasada ta jest więc bardziej odniesieniem do czystej logiki. Kolejny przykład pokazuje jej poprawne zastosowanie. Implementuje logikę narzędzia najważniejszego dla programisty: ekspresu do kawy:

Przykład kodu ekspresu do kawy zgodny z zasadą podstawienia Liskov: każdy typ ekspresu parzy kawę

Kawa zawsze jest parzona w ekspresie, niezależnie od jego typu.

Kolejna zasada to segregacja interfejsów.

Bardzo dobrym jej opisem jest zdanie: „wiele interfejsów dla konkretnych klientów jest lepsze niż jeden interfejs ogólnego przeznaczenia”.

W praktyce oznacza to, że tworzone interfejsy powinny być dedykowane tylko jednej funkcjonalności.

Przykład złamania tej zasady wygląda następująco:

Przykład kodu interfejsu drukowania raportów z metodami PDF i Excel, łamiący zasadę segregacji interfejsów

W powyższym przykładzie zaimplementowano logikę drukowania różnych raportów. Utworzono interfejs z dwiema deklarowanymi metodami: do drukowania PDF i arkuszy Excel. Do wyników szkolnego egzaminu arkusz Excel nie jest jednak potrzebny.

W tym przypadku ta metoda jest zbędna, ale skoro wymusza ją interfejs, musi zostać zaimplementowana. Zgodnie z zasadą segregacji interfejsów lepszym rozwiązaniem jest zadeklarowanie kilku wyspecjalizowanych interfejsów.

Aby rozwiązać problem, możemy utworzyć dwa osobne interfejsy: PrintablePdfInterface i PrintableExcelInterface.

Ostatnia zasada SOLID to odwrócenie zależności. Mówi, że moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu, a oba powinny zależeć od abstrakcji, a nie od konkretnych typów. Brzmi to skomplikowanie, ale to prosty sposób deklarowania funkcjonalności tak, aby działała z dowolną implementacją interfejsu. Dzięki temu kod łatwiej testować i zmieniać.

Poniższe przykłady dobrze to ilustrują. Złamanie zasady:

Przykład kodu łamiący zasadę odwrócenia zależności

Zastosowanie zasady:

Przykład kodu stosujący zasadę odwrócenia zależności

Powyższe zasady należy traktować jako zestaw narzędzi do pisania dobrego, solidnego kodu. Nie trzeba jednak stosować wszystkich elementów w każdym projekcie. Staraliśmy się zawrzeć dobre praktyki programowania.

Programiści powinni dostosować to narzędzie do indywidualnych potrzeb aplikacji. Intuicyjnie w małym projekcie ostatnich trzech zasad możemy nie potrzebować w formalny sposób, choć podstawienie Liskov dotyczy każdego użycia dziedziczenia, ale dwóch pierwszych zdecydowanie warto się trzymać, nawet przy budowie mniejszych funkcjonalności.

Z własnego doświadczenia wiem, że kod pisany od początku zgodnie z dobrymi praktykami znacznie łatwiej się rozwija.

Jak SOLID i KISS stosują się do rozwoju WordPressa i WooCommerce?

Te same idee działają w kodzie WordPressa. Akcje i filtry pozwalają rozszerzać zachowanie bez edycji rdzenia WordPressa ani plików wtyczki, co jest zasadą otwarte-zamknięte w praktyce, a motywy potomne działają według tej samej logiki. Utrzymywanie każdej klasy wtyczki skupionej na jednym zadaniu i zależność od interfejsów zamiast klas konkretnych ułatwia testowanie własnego kodu i przekazanie go innemu zespołowi. Przykłady znajdziesz w artykułach jak stworzyć własny motyw WordPress oraz czym są endpointy WooCommerce.

Odwrotny wzorzec widzimy w sklepach, które zmieniły właściciela: zaśmiecony kod, którego nikt nie śmie ruszyć. Audyt techniczny pokazuje, jak głęboki jest problem, a projekt ratunkowy stabilizuje kod przed jakąkolwiek przebudową. Przy nowych pracach zobacz nasze wdrożenia WordPress i sklepy WooCommerce.

Najważniejsze wnioski

  • Zachowaj prostotę: wybieraj najprostszy projekt, który rozwiązuje problem (KISS)
  • Daj każdej klasie lub metodzie jedną odpowiedzialność
  • Rozszerzaj zachowanie przez dodawanie kodu, a nie zmianę przetestowanego kodu
  • Dopilnuj, by klasy pochodne mogły zastąpić klasy bazowe bez niespodzianek
  • Preferuj małe, wyspecjalizowane interfejsy i zależ od abstrakcji

Najczęstsze pytania o SOLID i KISS

Co oznacza skrót SOLID?

SOLID oznacza: Single responsibility (jedna odpowiedzialność), Open/closed (otwarte-zamknięte), Liskov substitution (podstawienie Liskov), Interface segregation (segregacja interfejsów) i Dependency inversion (odwrócenie zależności). To pięć zasad projektowania obiektowego, które ułatwiają czytanie, rozszerzanie i testowanie kodu.

Co oznacza KISS w programowaniu?

KISS oznacza „keep it simple, stupid”. Zachęca do wyboru najprostszego projektu, który rozwiązuje problem, aby kod był łatwiejszy do zrozumienia, utrzymania i naprawy.

Czym różni się KISS od SOLID?

KISS to ogólna zasada unikania zbędnej złożoności. SOLID to konkretny zestaw pięciu zasad projektowania kodu obiektowego. Dobre stosowanie SOLID zwykle wspiera KISS, bo utrzymuje klasy małe i wyspecjalizowane.

Kto stworzył zasady SOLID?

Robert C. Martin (Uncle Bob) zebrał i spopularyzował zasady na początku lat 2000. Akronim stworzył Michael Feathers, a poszczególne zasady wywodzą się z wcześniejszych prac, takich jak zasada otwarte-zamknięte Bertranda Meyera i zasada podstawienia Barbary Liskov.

Czy w małym projekcie potrzebuję wszystkich pięciu zasad SOLID?

Niekoniecznie w formalny sposób. Jedna odpowiedzialność i otwarte-zamknięte popłacają nawet w małych funkcjach. Podstawienie Liskov ma znaczenie zawsze, gdy używasz dziedziczenia, a segregacja interfejsów i odwrócenie zależności zyskują na znaczeniu wraz z rozwojem kodu.

Jak zasady SOLID stosują się do rozwoju WordPressa i WooCommerce?

Haki WordPressa pozwalają rozszerzać zachowanie bez edycji plików rdzenia lub wtyczek, co jest zasadą otwarte-zamknięte w praktyce. Utrzymywanie wyspecjalizowanych klas wtyczki i zależność od interfejsów zamiast klas konkretnych ułatwia testowanie i utrzymanie własnego kodu.

Pwel Zmyslowski

Paweł Zmysłowski

CEO WLC.team

W WLC odpowiada za proces sprzedaży i zespół sprzedaży, a w przypadku bardziej złożonych projektów pełnię również rolę analityczną i doradczą.

Strona autora

Czy Twój WordPress „działa, ale jest wolny”?

WIĘCEJ ARTYKUŁÓW

Czytaj także

  • Why do my comparison tables cause layout shift?
    10 minuta

    Dlaczego moje tabele porównawcze powodują przesunięcia układu?

    Tabele porównawcze często powodują przesunięcia układu, bo ich części ładują się bez zarezerwowanego miejsca, więc treść skacze w trakcie renderowania strony. Dzieje się tak, gdy obrazy nie mają wymiarów, dane dynamiczne ładują się bez placeholderów lub własne czcionki przeformatowują tekst po pierwszym renderowaniu. Przesunięcia układu szkodzą doświadczeniu użytkownika i Core Web Vitals, które wpływają na…

    Czytaj

  • How do you implement multiple payment gateways in iGaming platforms?
    11 minuta

    Bramka płatności iGaming: integracja wielu bramek, poradnik

    Wdrożenie wielu bramek płatności w platformach iGaming polega na zintegrowaniu kilku procesorów płatności, aby oferować różne opcje płatności, zapewnić zgodność z przepisami i zmaksymalizować wskaźniki sukcesu transakcji. To podejście wymaga starannego planowania technicznego, silnych zabezpieczeń i inteligentnych systemów routingu. Proces zwykle obejmuje integracje API, zarządzanie bazą danych i dokładne testy, aby stworzyć płynne doświadczenia płatnicze…

    Czytaj

  • 1 minuta

    Rozwój działalności eksportowej na nowych rynkach zagranicznych

    White Label Coders sp. z o.o. otrzymała dofinansowanie z Unii Europejskiej na projekt „Rozwój działalności eksportowej na nowych rynkach zagranicznych”. Wsparcie w formie bonu eksportowego pochodzi z projektu „InterGlobal – internacjonalizacja przedsiębiorstw sektora MŚP z województwa śląskiego”, realizowanego przez Fundusz Górnośląski S.A. w ramach programu Fundusze Europejskie dla Śląskiego 2021–2027. Cele projektu Wejście z nowym…

    Czytaj