Maciej Aniserowicz's Blog, page 7
March 19, 2020
Modularyzacja a Estymacje [DNA Team LIVE]
The post Modularyzacja a Estymacje [DNA Team LIVE] appeared first on devstyle.pl.
March 18, 2020
200 tysięcy w jeden dzień, czyli : zapraszam na lekcję biznesu online! [vlog #344]
The post 200 tysięcy w jeden dzień, czyli : zapraszam na lekcję biznesu online! [vlog #344] appeared first on devstyle.pl.
March 17, 2020
Biznes w takich czasach [vlog #343]
The post Biznes w takich czasach [vlog #343] appeared first on devstyle.pl.
March 16, 2020
Życie w takich czasach [ #vlog #342 ]
The post Życie w takich czasach [ #vlog #342 ] appeared first on devstyle.pl.
March 11, 2020
Kumulacja Katastrof [ #vlog #341 ]
The post Kumulacja Katastrof [ #vlog #341 ] appeared first on devstyle.pl.
March 9, 2020
10 miesięcy i 300 tysięcy później… [vlog #340]
The post 10 miesięcy i 300 tysięcy później… [vlog #340] appeared first on devstyle.pl.
March 8, 2020
O kohezji słów kilka
Jak zapewne pamiętasz, ostatnio napisałem, że coupling jest pojęciem dość abstrakcyjnym. Kohezja – zwana też spójnością – bije go w tym na głowę. Jak mam stwierdzić, czy moja klasa jest spójna, czy wręcz przeciwnie? Pewnie nieraz myślałeś: „Moja na pewno jest spójna, ale ta kolegi obok spójności nawet nie stała”.
Z kohezją od początku był problem, bo nawet nie wiedziano, jak ją nazwać. Była określana jako „functional relatedness” (czyli „powiązanie funkcjonalne”), jednak ten termin został później uznany za „niezdarny”. Proponowano też inne nazwy, takie jak „binding” czy „functionality”. Glenford Myers z kolei w Reliable software through composite design nazywał ją „siłą”, co w gruncie rzeczy dobrze oddaje znaczenie samego konceptu, bo jest to siła, z jaką powiązane są elementy konkretnej klasy.
Glenn Vanderburg opublikował na swoim blogu post, w którym wytłumaczył, dlaczego jego zdaniem programiści mają problem ze zrozumieniem tego konceptu. Jego głównym argumentem było właśnie to, że nazwa sama w sobie jest źle dobrana, bo nie jest tak opisowa jak na przykład „coupling” oraz że rzadko się ją stosuje w języku angielskim. Przez to ciężko jest mieć punkt odniesienia do sytuacji z życia codziennego. Jako swój sposób na lepsze zwizualizowanie, o co chodzi w tym pojęciu, Vanderburg przedstawił metodę polegającą na zestawieniu słowa „cohesion” z innymi wyrazami o tym samym rdzeniu („hesion”). Na przykład: „kohezja” pochodzi od tego samego słowa co „adhezja”, oznaczająca lepkość. Jeśli coś się lepi do czegoś innego, to jest to jednostronne i często spowodowane tym, że użyliśmy czegoś zewnętrznego, co tę lepkość powoduje, czyli kleju. Jako wzór Vanderburg podał taśmę klejącą, która nie musi pasować całkowicie do tego, do czego się przyczepi. Rzeczy, które są spójne (charakteryzują się wysoką kohezją), nie potrzebują niczego zewnętrznego, żeby się połączyć, ponieważ dobrze do siebie pasują.
Kohezja jest tym większa, im lepsze mamy zrozumienie problemu, dlatego tak ważne jest jego dogłębne przeanalizowanie. Naszym celem jest odpowiednie grupowanie elementów. Dzięki temu zacieśniamy relacje, jakie między nimi występują.
Wszystko, co dotychczas napisałem o kohezji, może nadal być rozumiane subiektywnie. Co zrobić, jeśli chcielibyśmy podejść do sprawy metodycznie, zamiast opierać się tylko na przeczuciu, że coś w klasie pasuje albo nie? Należałoby odpowiedzieć na pytanie: „Co powoduje grupowanie elementów w konkretną klasę?”. A bardziej szczegółowo: „Jakimi kryteriami grupowania się kierujemy?”. Teraz właśnie postaram się zaprezentować te kryteria w kolejności od charakteryzujących się najmniejszą kohezją do tych z największą.
Coincidental cohesion
Pierwsze kryterium podziału to w zasadzie jego brak, czyli całkowita przypadkowość w doborze elementów znajdujących się w klasie. Innymi słowy – wolna amerykanka. Takie klasy powstają mimowolnie, a nie w procesie designu.
Klasy z taką kohezją zazwyczaj nie występują, a jeśli już, to najczęściej są artefaktami modularyzacji na siłę. Decydujemy się podzielić system dla samego dzielenia albo gdy zauważymy, że konkretna sekwencja wywołań się powtarza. Postanawiamy wtedy zamknąć ją w jakąś klasę, chociaż kawałki kodu, które chcemy wydzielić, w każdym z użytych kontekstów mają zupełnie inne znaczenie. Z czasem okazuje się, że dla każdego z tych kontekstów musimy wprowadzić jakieś zmiany w tej klasie.
Listing 1. Przykład przypadkowości w doborze elementów.
View the code on Gist.
Logical cohesion
Elementy spaja to, że rozwiązują taką samą klasę problemów. Dzięki temu jest coś, co logicznie łączy je ze sobą, ale wykonują one różne funkcjonalności.
Dobry przykład stanowią operacje matematyczne, które mogą być całkowicie od siebie niezależne. Każda z nich wykonuje inną funkcjonalność, a jednak zgrupowane są w ramach jednej klasy z… operacjami matematycznymi.
Listing 2. Wszystkie te metody łączy jedynie to, że są operacjami matematycznymi.
View the code on Gist.
Temporal cohesion
Jeśli dodatkowo zwiążemy elementy czasem, w którym mają zostać wywołane, to otrzymamy spójność czasową.
Najlepszymi przykładami są moduły/klasy inicjalizujące, zatrzymujące lub czyszczące. Tak oto na przykład funkcje inicjalizujące są ze sobą logicznie związane, ponieważ spina je funkcja, jaką pełnią (inicjalizują coś). Ponadto elementy te są ze sobą powiązane czasowo, bo muszą być wywołane w tym samym oknie czasowym – w tym przypadku – czasie inicjalizacji.
Często jest tak, że niektóre moduły po prostu muszą charakteryzować się tego typu kohezją i trudno zakodować je inaczej.
Listing 3. Przykład spójności czasowej.
View the code on Gist.
Procedural cohesion
Pojawia się wtedy, gdy zamiast modelować domenę problemu, koncentrujemy się wyłącznie na samym algorytmie wykonania – na przepływie kontroli. Ostatecznie prowadzi to do modelowania samego algorytmu – następujących po sobie kroków – a nie problemu, który rozwiązujemy.
Przez to, że elementy w module o spójności proceduralnej są związane ze sobą kolejnością występowania i czasem, kohezja między nimi jest jeszcze większa niż w przypadku spójności czasowej. Tam kolejność nie była istotna.
Spójność proceduralna charakteryzuje się występowaniem wielu pętli, warunków, kroków. Są to często klasy, których analiza sprawia, że chcemy sięgnąć po kartkę i ołówek, aby rozrysować schemat blokowy.
Communicational cohesion
Pierwszym typem kohezji, który koncentruje się na modelowanym problemie, jest spójność komunikacyjna. Tu kryterium grupowania jest oparte na tym, że elementy klasy są ze sobą powiązane komunikacyjnie, czyli że operują na tych samych danych wejściowych lub zwracają te same dane wyjściowe. Dzięki temu klasa może być łatwiej użyta w różnych kontekstach.
Tego typu klasy często powstają jako wynik myślenia o tym, jakie operacje można wykonać na konkretnym zbiorze danych lub w kontekście operacji, które muszą być wykonane, aby uzyskać konkretny wynik – konkretne dane wyjściowe.
Listing 4. Repozytorium jako przykład spójności komunikacyjnej.
View the code on Gist.
Sequential cohesion
W momencie gdy głównym kryterium przynależności elementów do klasy staje się sekwencja przetwarzania danych, mamy do czynienia ze spójnością sekwencyjną. W praktyce może to wyglądać tak, że dane wyjściowe jednego elementu przetwarzającego są danymi wejściowymi kolejnego. Aktywność konkretnego elementu musi być poprzedzona aktywnością innego – w odpowiedniej sekwencji.
Może tu wystąpić jednak mały problem. Kiedy mamy taką sekwencję przetwarzania – łańcuch wywołań – możemy w różny sposób ten łańcuch pogrupować. Dlatego klasy z tym typem kohezji mogą wykonywać więcej niż jedną funkcjonalność albo wręcz odwrotnie – część funkcjonalności. Z tego właśnie powodu spójność sekwencyjna stoi niżej w hierarchii od ostatniego typu – spójności funkcjonalnej.
Functional cohesion
Mamy z nią do czynienia wtedy, gdy elementy w klasie są tak dobrane, aby najlepiej jak to możliwe realizować konkretne zadanie – funkcjonalność. W takiej klasie każdy element przetwarzający stanowi jej integralną część i jest kluczowy dla funkcjonalności, którą klasa ma realizować. Klasa wykonuje nie mniej i nie więcej niż jedną funkcjonalność.
Co ciekawe, jako przykład podawane są często pojedyncze operacje matematyczne. Można sobie wyobrazić, że tworzymy klasę per operacja matematyczna. Dla porównania: w przypadku spójności logicznej tworzyliśmy klasę, która była zbitką różnych operacji.
O ile można łatwo stwierdzić, czy klasa charakteryzuje się tym rodzajem kohezji, o tyle w przypadku drobnych funkcjonalności, przy wyższych poziomach abstrakcji, często trudno jest to określić. Dlatego też robi się to przez porównywanie i sprawdzanie, czy klasa nie ma „defektów” niższych poziomów kohezji. Ot, taka metoda przez eliminację, podobnie jak to było w kontekście data couplingu.
Listing 5. Klasa prawdopodobnie wykonuje nie mniej i nie więcej niż jedną funkcjonalność.
View the code on Gist.
Przy przedstawionym podziale na typy kohezji warto wspomnieć o jeszcze jednej rzeczy, na którą zwrócili uwagę Larry Constantine i Edward Yourdon. Choć bardzo niechętnie, zaproponowali oni przypisanie wagi każdemu z typów. Niechętnie, ponieważ nie chcieli, żeby były one używane do jakichkolwiek szczegółowych analiz czy też rzutowały w jakikolwiek sposób na przyszłe badania nad kohezją. Niemniej chcieli pokazać, że wartości wag w ich odczuciu rozkładają się nieliniowo:
0: coincidental,
1: logical,
3: temporal,
5: procedural,
7: communicational,
9: sequential,
10: functional.
Ma to na celu jedynie pomóc w podejmowaniu decyzji projektowych, gdy uda nam się poprawnie zidentyfikować poszczególne typy kohezji w naszym kodzie. Zauważmy, że według skali spójność sekwencyjna jest tak bliska ideału, że nieraz nie warto dążyć do spójności funkcjonalnej. W zamian za to lepiej poświęcić energię na poprawę miejsc, gdzie zysk będzie większy.
Czas na rozciąganie!
Na koniec chciałbym jeszcze wspomnieć o kilku prostych ćwiczeniach, które mogą zarówno pomóc określić typ, jakim charakteryzuje się klasa, jak i ułatwić osiagnięcie spójności klas.
Twórcy pojęcia „kohezja” zaproponowali technikę, która polega na zadaniu pytania: „Jaką właściwie operację wykonuje klasa (moduł)?”, a następnie na przedstawieniu tej operacji w postaci jednego zdania.
I tak:
Operacja, jaką wykonuje klasa, która z natury charakteryzuje się spójnością funkcjonalną, powinna dać się opisać prostym zdaniem.
Na przykład:
„Oblicz VAT”.
„Oblicz prowizję”.
„Odczytaj temperaturę”.
„Pobierz dane zamówienia”.
Jeśli jedyną rozsądną drogą do opisania operacji klasy jest użycie zdania złożonego (z przecinkami, zawierającego wiele czasowników), to prawdopodobnie mamy niższą kohezję niż funkcjonalna i może to być spójność sekwencyjna, komunikacyjna lub logiczna.
Na przykład:
„Zaktualizuj czas spędzony nad zadaniem, ilość przepracowanego czasu pracownika i wartości na fakturze – wszystko na podstawie karty czasu pracy” – spójność komunikacyjna, ponieważ wszystkie elementy są powiązane ze sobą komunikacyjnie przez kartę czasu pracy.
„Zaktualizuj zamówienie i zapisz je” – sekwencyjna.
Jeśli opis zawiera słowa zorientowane na czas, takie jak „najpierw”, „następnie”, „później”, „wtedy”, „na początek”, „kiedy”, „dopóki”, to często mamy do czynienia ze spójnością czasową albo proceduralną.
Przykłady:
„Przed sortowaniem zapisz dane, usuń duplikaty i sprawdź sumy kontrolne” – spójność czasowa, ponieważ zakładamy, że kolejność w tym przypadku nie jest istotna.
„Ściągnij kursy walut, następnie wyślij wyniki sprzedaży do działu sprzedaży i nowe zamówienia do działu zamówień” – prawdopodobnie spójność proceduralna, bo modelujemy algorytm procesu, gdzie kolejność jest istotna, jednak dane wyjściowe nie są zarazem danymi wejściowymi kolejnych elementów, nie jest to zatem spójność sekwencyjna.
Jeśli po czasowniku występuje rzeczownik w liczbie mnogiej, to może to być spójność logiczna:
„Zbierz statystyki: ilości wywołanych metod, ilości wyjątków, zużycie procesora” – elementy są związane ze sobą logicznie wyłącznie tym, że są statystykami, chociaż każda z nich ma całkowicie inną charakterystykę.
Gdy występują słowa takie jak „zainicjalizuj”, „posprzątaj”, „utrzymaj”, to też często jest to spójność czasowa.
A co, jeśli chcielibyśmy określić, czy coś powinno należeć do klasy, która posiada już jakieś elementy? Można to zrobić, korzystając z zasady łączności, czyli przez analizę, czy dokładany element pasuje do pozostałych. Przykładową techniką jest po prostu próba ułożenia sobie w głowie zdania „Mój nowy element Z jest powiązany z klasą zawierającą X i Y, bo X, Y i Z są powiązane ze sobą ze względu na posiadanie takiej a takiej właściwości” – w tej lub podobnej formie.
Na koniec warto też dodać, że jeśli modelujemy jakiś problem, to najlepiej jest to robić w większym gronie. Dlaczego? Wspominałem, że kohezja będzie tym większa, im większe mamy zrozumienie problemu. Tutaj jest jednak pewien haczyk – każdy z nas, analizując dany problem, może mieć w głowie całkowicie inne jego wyobrażenie. A co za tym idzie – inny sposób jego rozwiązania. Dlatego tak ważne jest, aby zrozumienie problemu w zespole było jak najbardziej spójne.
Jak żyć?
Co zrobić, gdy kolega zarzuci nam na code review: „Twoja klasa charakteryzuje się niską kohezją”? Już czujemy, jak skacze nam adrenalina. Możemy zareagować różnie – uciec, walczyć albo… porozmawiać.
Zakładając, że nasz kolega nie jest dzikim zwierzęciem i po code review nie rozerwie nas na strzępy, spróbujmy skorzystać z opcji numer trzy. Może to jest właśnie sygnał do tego, aby podyskutować w zespole o tym, co rozumiemy pod pojęciem kohezji, a następnie – na jakie jej typy możemy sobie pozwolić w konkretnych miejscach naszego systemu.
The post O kohezji słów kilka appeared first on devstyle.pl.
March 5, 2020
Nie doceniamy, jak wiele możemy zrobić w długim czasie (albo odwrotnie) [vlog #339 LIVE]
The post Nie doceniamy, jak wiele możemy zrobić w długim czasie (albo odwrotnie) [vlog #339 LIVE] appeared first on devstyle.pl.
March 1, 2020
DevTalk #112 – O Progressive Web Applications z Adamem Barem
Nie trzeba się martwić, jak aplikacja wygląda na różnych urządzeniach. Jeden kod źródłowy wystarczy, by zawojować wszystkie systemy operacyjne. Mniej zachodu przy aktualizacjach niż w przypadku apek natywnych… Nic dziwnego, że o Progressive Web Applications mówi się z coraz większym entuzjazmem!
Jednak czy PWA to tylko cud i miód? W sto dwunastym odcinku DevTalka przyjrzymy się temu rozwiązaniu pod czujnym okiem eksperta – Adama Bara.
Adam jest trenerem technologii frontendowych w Bottega IT Minds, pasjonatem Mobile Weba, twórcą What Web Can Do Today – serwisu o możliwościach Weba na urządzeniach mobilnych, a także niezależnym konsultantem i fullstack deweloperem doświadczonym w kilku stosach technologicznych, ostatnio wiernym TypeScriptowi.
Z tego odcinka dowiesz się:
Czym są PWA?
Jakie są różnice między PWA a RWD?
W jakim języku pisze się PWA?
Na co trzeba uważać tworząc PWA?
Jakie możliwości i ograniczenia mają takie aplikacje?
PWA vs. Xamarin/Flutter – co lepsze?
PS Podobał Ci się ten odcinek? Zostaw gwiazdki i opinię na na iTunes. To mały gest, a stanowi dla mnie wielką pomoc. Dzięki!
Do startu… Gotowi… PLAY!
http://traffic.libsyn.com/devtalk/DevTalk_112_-_PWA_z_Adamem_Barem.mp3
Montaż odcinka: Krzysztof Śmigiel.
Ważne adresy:
zapisz się na newsletter
zasubskrybuj w iTunes, Spotify lub przez RSS
ściągnij odcinek w mp3
Linki:
Adam
blog Adama
What Web Can Do Today
artykuł Adama o poprawnej implementacji aktualizacji Service Workerów
zwięzłe wprowadzenie do PWA na web.dev
ścieżki nauki o tworzeniu aplikacji webowych na web.dev
szkolenie Adama z zakresu PWA
DevTalk
O Xamarin z Jakubem Jędryszkiem
O Flutterze z Dominikiem Roszkowskim
O Androidzie z Pauliną Szklarską
O frontendzie z Maciejem Korsanem
O błędach w tworzeniu stron WWW z Tomaszem “Commander” Jakutem
Narzędzia
PWA manifest generator
PWA service worker templates
Muzyka wykorzystana w intro:
“Misuse” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0
http://creativecommons.org/licenses/by/3.0/
The post DevTalk #112 – O Progressive Web Applications z Adamem Barem appeared first on devstyle.pl.
February 23, 2020
Sześć twarzy couplingu
Ostatnio przyjrzeliśmy się symptomom gnijącego designu oraz chorobie, która jest jego główną przyczyną – złemu zarządzaniu zależnościami. Wysnułem wniosek, że w leczeniu pomaga dążenie do designu, w którym mamy niski coupling i wysoką kohezję. Dlatego dzisiaj opowiem o couplingu i jego typach, jak je rozpoznawać i jakie to ma konsekwencje dla Twojego designu. Zapnij pasy i startujemy!
Z couplingiem wiąże się pewien problem: każdy z nas może zupełnie inaczej rozumieć to, czy coś jest mocno/słabo powiązane. Innymi słowy, pojęcie to po prostu jest wysoce abstrakcyjne. A jak sobie często radzimy z abstrakcjami? Szukamy analogii do życia codziennego. Można się o tym bardzo szybko przekonać, przeglądając wysoko oceniane odpowiedzi na StackOverflow. Roi się tam od porównań typu:
„iPods, iPads are a good example of tight coupling” – silny coupling; podzespoły są tak mocno powiązane ze sobą, że zwykła wymiana baterii może być bardzo czasochłonna i kosztowna.
„Car and spare parts analogy of coupling” – przykłady silnego i słabego couplingu; niektóre części łatwo wymienić, inne nie.
„You and your wife” – silny coupling.
Analogie są fajne. Pozwalają nabrać kontekstu, załapać, o co mniej więcej chodzi. Na rozmowę rekrutacyjną jak znalazł. Ale czy faktycznie wszystko już jest jasne? Nie do końca…
Warto się cofnąć w czasie do roku 1974, kiedy to trzech panów: Larry Constantine, Glenford Myers oraz Wayne Stevens, opublikowało artykuł Structured design w trzynastym wydaniu „IBM Systems Journal”. W publikacji tej zostały dokładnie zdefiniowane i opisane koncepty zarówno couplingu, jak i kohezji. Za ich ojca uważa się pierwszego z wymienionej wyżej trójki.
Coupling to metryka opisująca, jak klasy są ze sobą powiązane – jaka jest siła powiązań między nimi. Jeśli dwie klasy są silnie powiązane, wtedy istnieje wysokie prawdopodobieństwo, że jeśli zmienimy coś w jednej, to będziemy musieli też zmienić coś w drugiej. Oczywiście to z kolei wpływa na koszty developmentu i utrzymania.
Tym samym powinno się dążyć do słabego couplingu, aby na przykład jedna osoba mogła przestudiować/debugować/utrzymywać daną klasę bez szczegółowej wiedzy o innych klasach w systemie. Im słabsze powiązania, tym klasy są bardziej niezależne. Ale jak określić, jak bardzo jedna klasa zależy od drugiej? Warto się przyjrzeć komunikacji między nimi. W jaki sposób to robią? Jak wygląda wiadomość, którą sobie wysyłają? Wiedząc to, będziemy mogli stwierdzić, jaki jest typ tego powiązania i jakie niesie to za sobą konsekwencje.
Oryginalny podział zaproponował Glenford Myers (drugi ze wspomnianej wcześniej trójki) w książce Reliable software through composite design. Wyróżnił on sześć typów couplingu i im właśnie poświęcimy teraz trochę uwagi. Przejdziemy od tych najmniej do najbardziej pożądanych.
Content coupling
Na początek content coupling, zwany również patologicznym. A to dlatego, że występuje wtedy, gdy jedna klasa sięga bezpośrednio do zawartości drugiej. Przez to jej obiekty manipulują wewnętrznym stanem obiektów innej klasy i są w stanie zmieniać ich zachowanie.
Jak to wygląda z poziomu wiadomości wymienianej między obiema klasami? Jest ona narzucana. Jesteśmy z nią tak nachalni, że wręcz wpływamy na wewnętrzny stan odbiorcy. W efekcie powoduje to, że odbiorca jakby nie może oprzeć się naszej wiadomości.
Przykłady:
nierespektowanie prywatnych modyfikatorów dostępu,
mechanizmy refleksji,
monkey patching – zmiana zachowania jakiejś zewnętrznej biblioteki w trakcie wykonania programu.
Jak dużo wiemy o innej klasie? W tym przypadku praktycznie wszystko.
Listing 1. Content coupling – obiekt klasy Image zapewne wolałby osobiście zarządzać wartością pola description
View the code on Gist.
Common coupling
Następnie mamy common coupling. Występuje on, gdy klasy opierają komunikację między sobą na jakimś globalnym, współdzielonym stanie, który może być przez nie zmieniany. Co za tym idzie – jeśli w czasie działania aplikacji stan się zmieni, to możemy nie wiedzieć, jak zareagują na tę zmianę obiekty innych klas, które z niego korzystają.
Przy tym typie powiązania, jeśli nie chcemy czegoś zepsuć, nadal musimy wiedzieć bardzo dużo o innych klasach. Musimy wiedzieć, jakie one są i jak ich obiekty zareagują na zmianę współdzielonego stanu.
External coupling
Rozważmy przypadek, kiedy klasy komunikują się ze sobą wiadomościami, których struktura przychodzi z zewnątrz. Mamy wtedy do czynienia z external couplingiem.
Na przykład: jeśli mamy jakąś klasę z zewnętrznej biblioteki, której używamy do komunikacji między dwoma klasami, to jesteśmy z nią powiązani. Nie mamy kontroli nad zmianami w zewnętrznej bibliotece i jeśli cokolwiek się w niej zmieni, może to mieć wpływ na nasze klasy.
Kolejnym przykładem może też być jakiś zewnętrzny format wymiany danych. Praktycznie każdy z naszych systemów integruje się z czymś zewnętrznym i zmuszeni jesteśmy wtedy komunikować się w narzucony sposób.
W obu wymienionych przykładach warto odcinać się od tych zależności najszybciej jak to możliwe i wewnątrz aplikacji posługiwać się strukturami, nad którymi mamy kontrolę. Dzięki temu ograniczymy liczbę miejsc, w których jesteśmy podatni na zmiany przychodzące z zewnątrz.
Control coupling
Wyobraź sobie sytuację, że piszesz kod i wywołujesz metodę jakiejś innej klasy, a metoda ta przyjmuje flagę, od której zależy wynik wywołania. Ty jako wywołujący musisz przeanalizować, w jaki sposób tę flagę ustawić, żeby otrzymać konkretny wynik. Jak widać, w tym przypadku klasa, której metodę wywołujesz, nie jest dla ciebie czarną skrzynką i musisz dość dużo o niej wiedzieć. Stawiasz się w tym momencie w pozycji koordynatora – mówisz, co ma zostać zrobione i czego oczekujesz w zamian. Jest to zachowanie charakterystyczne dla control couplingu. Jedna klasa przekazuje do drugiej elementy kontroli, czyli takie, które mają wpłynąć na wykonanie jej logiki oraz ostatecznie na zwracany wynik. Najczęściej elementami kontroli są właśnie różnej maści flagi czy typy wykorzystywane w instrukcji switch.
W programowaniu zorientowanym obiektowo to obiekt powinien decydować o tym, co ma zostać zrobione i zwrócone, na podstawie swojego własnego stanu i otrzymanych danych, a nie na podstawie tego, co ktoś sobie życzy.
Listing 2. Może warto byłoby rozbić tę metodę na dwie różne?
View the code on Gist.
Stamp coupling i data coupling
Gdy osiągamy stan, w którym komunikacja nie zachodzi w żaden z wcześniej wspomnianych sposobów, czyli nie jest patologiczna (content coupling), nie odbywa się przez globalny stan (common coupling) ani przez zewnętrzny protokół (external coupling) oraz nie narzucamy, co ma zostać zwrócone (control coupling), to dochodzimy do dwóch typów couplingu, które na pierwszym miejscu stawiają samą wiadomość – to, jaką ona przybiera formę.
W pierwszym z nich (stamp coupling) klasy komunikują się za pomocą wewnętrznej struktury danych (najczęściej poprzez różnego rodzaju DTO). Odbiorca wiadomości ostatecznie może zdecydować się na wykorzystywanie tylko niektórych pól całej struktury. Reszty może w ogóle nie potrzebować.
Często w systemie możemy spotkać się z klasami, których nazwa kończy się angielskim słowem „details” albo „data”. Brzmi znajomo? Nie? No to przykład: mamy klasę EmployeeData. Jak już się pewnie domyślasz, pod tą przewrotną nazwą może kryć się wszystko – sky is the limit. Mamy też metodę, która otrzymuje obiekt takiej klasy, a następnie zwraca adres tytułowego klienta. Wykorzystuje ona jednak z całej tej struktury tylko jedno pole – jego identyfikator. Można by się teraz zastanowić, jakie powiązanie tutaj występuje. Przecież nadawca jedynie tworzy jakiś obiekt z danymi i przesyła go odbiorcy. Mamy tu do czynienia z couplingiem do struktury samej w sobie. Zmiana może nastąpić w polach struktury, które w konkretnym kontekście nie są wykorzystywane.
Ten typ couplingu może też prowadzić do powstawania sztucznych struktur, które będą trzymały w sobie dane niezwiązane ze sobą. Dochodzimy wtedy do momentu, kiedy widząc taki „worek”, stwierdzamy, że dorzucenie kolejnych nie zaszkodzi.
Zastanówmy się, jak możemy uniknąć powiązania do struktury, która może zawierać w sobie wiele danych – niekoniecznie potrzebnych. Odpowiedź jest prosta: zamiast używać struktury, przesyłajmy wyłącznie dane. W taki oto sposób dojdziemy do najluźniejszego typu couplingu, którym jest data coupling.
W praktyce wygląda to tak, że metoda przyjmuje listę argumentów opisujących wartości. Są one kluczowe dla działania metody. Innymi słowy – nie ma tam miejsca na przekazywanie zbędnych elementów. Na przykład: metoda zwracająca kod pocztowy klienta, jako parametr wywołania, zamiast wcześniej wspomnianej struktury EmployeeData otrzymuje wyłącznie to, czego potrzebuje, czyli tylko sam identyfikator klienta.
Opisaną sytuację oraz różnicę między tymi dwoma typami couplingu można zaobserwować na poniższym listingu.
Listing 3. Stamp coupling i data coupling
View the code on Gist.
W tym momencie możemy usłyszeć głos naszego wewnętrznego malkontenta: „Eee tam, gdybyśmy wszędzie używali tylko wbudowanych typów języka zamiast struktur danych, popadlibyśmy w primitive obsession”. Bardzo dobrze, że masz takie wątpliwości, dlatego też liczę na Twoje pragmatyczne podejście do tematu. Nie zmienia to jednak faktu, że coupling jest silniejszy, gdy przekazujemy strukturę danych, niż gdy przekazujemy konkretne wartości. Unikamy wtedy powiązania do samej struktury wiadomości.
Podsumowanie typów
Jeśli chodzi o typy couplingu, to już prawie wszystko. Chciałbym podkreślić jedną rzecz – podział, który przedstawiłem (według Myersa), to podział oryginalny. Kładzie się w nim nacisk na komunikację i treść tej komunikacji, czyli samą wiadomość. Piszę to, ponieważ są jeszcze typy, które wynikają stricte z obiektowego paradygmatu programowania. I to właśnie te typy są nam chyba najbardziej znane. Chodzi tu o to, jak fizycznie zostali ze sobą połączeni nadawca i odbiorca. Czy nadawca „stworzył” odbiorcę, czy też nadawca operuje na jego interfejsie, czy w końcu nadawca rzuca swoją wiadomość w świat, nie wiedząc, kto na nią zareaguje.
Typy couplingu według podziału Myersa zostały zdefiniowane, gdy wiodącym paradygmatem programowania był paradygmat proceduralny. Później, kiedy liderem stało się OOP, wydaje się, że zapomniano o nich, zwracając bardziej uwagę na nowe typy charakterystyczne dla programowania zorientowanego obiektowo. Mimo to oryginalny podział jest nadal bardzo aktualny. Skąd wniosek, że o nich zapomniano? Gdy popatrzymy na kawałki kodu w internecie, które znajdujemy pod hasłem „przykłady słabego/mocnego couplingu”, to w przeważającej liczbie przypadków widzimy kod, w którym rozważa się to wyłącznie na poziomie interfejs (słaby coupling) kontra konkretna implementacja (mocny coupling). Czyli na poziomie, który jest charakterystyczny właśnie dla OOP. Ale to temat na zupełnie inną dyskusję.
Decoupling
Mówiąc o couplingu, warto też wspomnieć kilka słów o decouplingu. Ma on dość prostą definicję: jest to jakakolwiek metoda lub technika, dzięki której moduły/klasy osiągną większą autonomię.
Każdy z typów couplingu sugeruje jakieś formy decouplingu. Ogólną zasadą może być po prostu faworyzowanie typów ze słabszym couplingiem. Na przykład: jeśli zidentyfikowaliśmy control coupling, bo mamy metodę, w której są dwa „przebiegi” i rezultat wywołania jest inny dla każdego z nich, to może udałoby się nam rozbić tę metodę na dwie mniejsze. Dzięki temu może uzyskalibyśmy nawet data coupling. Innym przykładem może być metoda, która przyjmuje jako parametr strukturę danych i wykorzystuje tylko jej małą część – może udałoby się ją zmienić tak, by przyjmowała tylko te dane, których potrzebuje, w postaci krótkiej listy argumentów.
Kolejną techniką może być projektowanie klasy w taki sposób, jakby komunikacja z nią odbywała się przez kolejkę. W takim podejściu koncentrujemy się na tym, co konkretna klasa ma robić i czego faktycznie potrzebuje. Odcinamy się tym samym od czasu – moment wykonania przestaje być tak istotny.
Dobrą praktyką jest też posługiwanie się w komunikacji między klasami strukturami – czy też wartościami charakterystycznymi dla konkretnego kontekstu. Innymi słowy, chodzi tu o to, żeby na przykład klasy w ramach swoich paczek komunikowały się między sobą „lokalnymi” strukturami danych, a niekoniecznie strukturami, które są dostępne globalnie.
Na koniec chciałbym przekazać jeszcze jedną bardzo ważną uwagę: couplingu nie powinno się rozpatrywać w kategorii tylko dobry lub tylko zły. Jeśli chodzi o design całej aplikacji czy systemu, powinniśmy dążyć do luźnego couplingu. Znajdą się jednak w nim pewne miejsca, gdzie będziemy potrzebować silniejszego couplingu, i próba jego rozluźniania nie będzie miała najmniejszego sensu. Musimy brać pod uwagę to, że coupling ma kilka rodzajów i każdy z nich jest inny. Musimy umieć je rozpoznawać i być świadomi konsekwencji swoich wyborów.
To tyle, jeśli chodzi o coupling. W kolejnym wpisie wracam z tematem kohezji. Porozciągamy się trochę!
The post Sześć twarzy couplingu appeared first on devstyle.pl.
Maciej Aniserowicz's Blog
- Maciej Aniserowicz's profile
- 22 followers
