GRASP

Patryk Woziński
5 min readMar 12, 2019

Tytułowy akronim jest obarczony przez wielu tajemnicą. Jego znajomy – SOLID to taka gwiazdeczka czerwonego dywanu, o której mówi się i pisze od … właściwie to od zawsze. Każdy chce pisać kod zgodny z tym standardem, a połowa ogłoszeń o pracę ma zawarte mniej więcej taki fragment tekstu:

Piszemy SOLIDny kod!

Dobrze, ale co to oznacza, że piszecie kod solidny? Czy spełnia on najważniejsze zasady dotyczące programowania obiektowego? Wiele osób odnosi się też jedynie do pierwszej litery wyżej wymienionego skrótu – mianowicie do Single Responsibility Principle, a w skrócie SRP. Pytając różne osoby o wyjaśnienie tego pryncypia dostaję odpowiedź: „zasada pojedynczej odpowiedzialności, czyli klasa ma robić tylko jedną rzecz” – co oznacza „robienie jednej rzeczy”? Czy jeżeli mój obiekt będzie miał możliwość dodawania posta, ale jednocześnie będzie sprawdzała poprawność danych w innym miejscu, to czy na pewno spełniam zasadę SRP?

Późniejszym wyjaśnieniem było takie, że zasada pojedynczej odpowiedzialności, to stworzenie klas, których edycja jest zawsze przeprowadzana w jednym celu, w jednym kierunku zmiany. Bliżej, fajniej ale powinniśmy znać granice i rozumieć kiedy klasa ma jeden powód do zmiany.

Sprawa ma się bardzo podobnie w przypadku innych zasad wywodzących się z grupy jaką jest SOLID. Często o tej całej reszcie zapominamy, zakładamy klapki na oczy i myślimy tylko S-ką.

Uważam SOLID za bardzo fajne podejście, którym powinniśmy się kierować, aczkolwiek jest jeszcze zbiór zasad związanych z poprawnym projektowaniem klas i relacji obiektów. Tym zbiorem jest GRASP.

Jakiś losowy obrazek, który znalazłem po wpisaniu słowa kluczowego “grasp”

General Responsibility Assignment Software Patterns

Nazwa brzmi bardzo dumnie: chciałoby się powiedzieć, że wpisanie jej w CV to dodatkowe +500 (plus z lewej strony, nie upolityczniajmy programowania) do umiejętności.

Czym jest, jak się za to zabrać i jakie płyną korzyści z korzystania z tych reguł? O tym już za chwilę!

Na co to komu?

GRASP to nic innego jak zbiór zasad i reguł, których należy się trzymać, aby móc pisać kod, którego nie będziemy wstydzili się przed kolegami i koleżankami. Powiązanym terminem jest Responsibility Driven Design.

GRASP i jego zrozumienie daje nam poczucie tworzenia aplikacji, których utrzymanie będzie tańsze, bo kod stanie się zgrabniejszy. Oczywiście z kodem jest jak z ogródkiem, nie możemy wszystkiego zasiać i nie zaglądać tam już nigdy. Kod, a szczególnie warstwa aplikacyjna i domenowa, to miejsca, które trzeba nieustannie pielęgnować, aby w razie potrzeby zmian być na to gotowym i móc bezpiecznie zacząć sesje refactoringowe lub optymalizacyjne.

Jakie zasady kryją się w naszej paczce?

GRASP to zbiór wielu dobrych praktyk, które postaram się w późniejszej części tego wpisu opisać. Zaczynamy, oto jadłospis na dzisiejszy… dobra, nie wiem czy czytać będziecie to wieczorem, czy też o poranku, ale słodycze każdy lubi, także oto słodyczospis.

  • Information Expert
  • Controller
  • Creator
  • High Cohesion
  • Low Coupling
  • Pure Fabrication
  • Polymorphism
  • Protected Variations
  • Indirection

Wybaczcie, ale jestem bardzo leniwym człowiekiem, więc w tym wpisie istnieje szansa, że nie opiszę każdej z reguł, a zrobię to na przykład w kolejnym tekście.

Information Expert

Moim zdaniem to najfajniejsza z reguł, dzięki której będziemy w stanie łatwo zrozumieć, który z obiektów powinien pełnić nasze odpowiedzialności i umożliwiać ich dokonywanie w dalszych etapach LifeCycle aplikacji. W największym uproszczeniu Expertem będzie obiekt, który ma największą wiedzę i ilość informacji, by móc wykonać konkretną operację. Klasa powinna zawierać w sobie informacje bez wyciągania czegokolwiek ze swoich sąsiadów (klas znajdujących się obok), które będą jej potrzebne do swojego zadania.

W poniższym przykładzie przedstawiłem tę (ale i nie tylko) zasadę, gdzie odpowiedzialność przeliczania kwoty zostaje oddelegowana do klasy Price, która to ma największą wiedzę na temat potrzebnych do operacji danych.

Konstrukcja niezgodna z regułą Information Expert
Konstrukcja zgodna z regułą Information Expert — wydelegowanie mnożenia do obiektu mającego największą wiedzę o tym w jaki sposób to zrobić

Creator

Ten pattern odpowiedzialny jest, tak jak jego nazwa idealnie wskazuje – za tworzenie innych obiektów. To dzięki niemu jesteśmy w stanie wyłonić miejsce, w którym powinny rodzic się inne instancje obiektów.

Klasa B jest odpowiedzialna za tworzenie instancji klasy A w momencie, gdy spełnione jest jedno z poniższych wymagań:

  • B zawiera A
  • B blisko współpracuje z A
  • B ma potrzebne dane do utworzenia instancji A

High Cohesion

Reguła ta określa w jaki sposób utworzyć obiekty z prostymi relacjami, których cel będzie bardzo transparentny. Dodatkowo obiekty stworzone zgodnie z tą regułą będą proste i łatwe w utrzymaniu. Idąc zgodnie z tą zasadą powinniśmy budować klasy, które odpowiedzialne będą tylko i wyłącznie za jedną, malutką rzecz, aby zmiany nie mogły zepsuć aplikacji w całkowicie innej jej części.

Low Coupling

Powiązanie i relacje między klasami to bardzo trudny do zrozumienia temat. Wielu developerów w codziennej pracy ma z tym problem i nie zastanawiając się zbyt wiele tworzy sieci zależności, które są niczym makaron spaghetti. Niezwykle ważne jest, aby trzymać jak najmniejszą ilość zależności między klasami, a dodatkowo utrzymywać jeden kierunek tych zależności, aby nigdy nie były one dwustronne. W momencie, gdy napotkamy już system, w którym zależności są dwustronne warto pomyśleć nad Inversion of Control— przykładowo poprzez oparcie zależności nie na konkretnej klasie, a jej interfejsie. Jeszcze silniejszą formą odwrócenia zależności jest wprowadzenie eventów. Najważniejsze, by pamiętać, że odwracając zależności i oddzielając się od nich — tracimy kontrolę. Wprowadzenie podejścia Event-Driven to także utrata kontroli nad czasem. Warto pamiętać, że można to rozwiązać poprzez wprowadzenie wzorca Sagi, ale to temat na całkowicie inny wpis.

Zależność pomiędzy klasami istnieje, w momencie gdy obiekt A posiada atrybut typu obiektu B, lub gdy obiekt A wywołuje metody obiektu typu B (nie jego kontraktu), lub gdy obiekt A dziedziczy obiekt B, lub w momencie powiązania typów i zwracania ich lub przyjmowania na wejściu.

Konstrukcja niezgodna z regułą Low Coupling — wiele bezpośrednich zależności od klasy Beta w klasie Alpha
Konstrukcja zgodna z reguła Low Coupling — usunięcie zależności poprzez wprowadzenie kontraktu

Controller

Kontrolerami w systemie są miejsca, które potrafią obsłużyć konkretne przypadki użycia. W podejściu CQRS będą to QueryHandler oraz CommandHandler, a zaś w podejściu wykorzystującym serwisy aplikacyjne będą to serwisy, które handlują nasz request input. Czasami tę odpowiedzialność pełnią kontrolery we wzorcu Model-View-Controller, aczkolwiek uważam, że lepiej jest oddzielić logikę przypadków użycia od warstwy UI jaką stanowią kontrolery w podejściu MVC.

Protected Variations

Reguła ta mówi, że wszelkie miejsca, w których mogą zajść zróżnicowania związane z implementacjami oraz może pojawić się niestabilność należy spiąć z użyciem kontraktu (interfejsu), do którego możliwe będzie wpięcie dowolnej implementacji w razie potrzeby. Każda kolejna, nowa wersja wykorzystująca kontrakt może zostać zaimplementowana w całkowicie inny sposób ze zróżnicowanymi zależnościami na miarę potrzeb. Dzięki tej zasadzie spełnimy także zasadę Open Close Principle z SOLID.

Indirection

Co w momencie, gdy nie chcemy tworzyć bezpośredniego powiązania między klasami? Według GRASP-a powinniśmy stworzyć klasę pośrednią, która będzie realizowała odpowiedzialności. Obiekty powinny być w jak najmniejszym stopniu ze sobą związane, aby nie uzależnić się od siebie.

Pure Fabrication

Reguła ta pomaga nam w momencie, gdy nie chcemy naruszyć zasad Low Coupling oraz High Cohesion, a zaś rozwiązanie sugerowane nam przez Information Experta nie jest satysfakcjonujące. Jest to często byt, który nie istnieje w realnym środowisku domeny, której odpowiedzialności staramy się zrealizować, aczkolwiek niesamowicie pomaga nam w zrealizowaniu pozostałych zasad, które osobiście uważam za ważniejsze, a w niektórych sytuacjach te byty nie mogą ze sobą koegzystować obok siebie.

Literatura?

Bardzo wiele informacji na temat samego GRASPa jak i podejścia do relacji między obiektami oraz poprawnej pracy na nich znajdziemy w książce Craiga Larmana pod tytułem “Applying UML and patterns”, która jest można powiedzieć białym krukiem na polskim rynku. Tytuł sugeruje, że książka traktuje o UML-ach, ale jest to jedynie jej kawałek. Podobno w czasach, gdy C.Larman wydawał tę książkę termin “UML” był bardzo chodliwy.

Na sam koniec chciałbym Was serdecznie zaprosić do mojego profilu na GitHubie gdzie znajdziecie wiele różności — klik.

--

--

Patryk Woziński

Product Engineer with many years of experience in creating and designing web applications. #DDD freak