Wprowadzenie
Tym razem krótka recenzja pewnej książki z roku 2006, a raczej jej polecenie każdemu projektantowi i architektowi dzisiaj. Na końcu polecam także kolejną nowszą pozycję jako uzupełnienie. Adresatami tej recenzji są głównie analitycy i projektanci, jednak adresuję ten wpis także do developerów, zakładam że dla nich nie jest to „coś nowego”, ale być może mają jakieś rady dla projektantów.
Warto także podkreślić, że pomiędzy OOP a OOAD jest coraz większa różnica i podział na role: analiza i projektowanie oraz implementacja, a także postępująca separacja tych ról, stają się standardem w inżynierii oprogramowania :
Kolejna warta zwrócenia uwagi rzecz: projektowanie integracji systemów w organizacji to nic innego model systemu zorientowany na interfejsy (patrz wcześniejszy wpis: Integracja systemów ERP jako źródło przewagi rynkowe).
Zanim jednak wczytacie się Państwo z detale, polecam krótki referat Martina Fowlera z 2015 roku:
Recenzja
Interface-Ofriented Design , to książka o architekturze i jej projektowaniu. We wstępie autor pisze (oryg):
Autor, na wielu przykładach pokazuje jak można projektować (tworzyć) oprogramowanie budując je ze współpracujących komponentów.
Wiele się mówi o programowaniu obiektowym a mało o projektowaniu zorientowanym na komponenty (moduły). Ważna rzecz: projektowanie zorientowane na interfejsy koncentruje się na komponentach i ich interfejsach, komponenty te mogą, ale nie muszą, być implementowane za pomocą języków obiektowych. Projekty, które skupiają się na komponentach i ich interfejsach mają ogromną zaletę: precyzyjnie opisują co ma program robić a jednocześnie nie narzucają narzędzia implementacji.
Kolejna rzecz to fakt, że obecnie znacznie bardziej niż w czasach gdy książka była pisana (ukazała się w 2006 roku), nabiera znaczenia rozproszona architektura: realnie nie ma już monolitów, mamy integracje systemów między partnerami handlowymi, z rejestrami publicznymi, między lokalnymi i chmurowymi infrastrukturami. Rozproszone przetwarzanie to architektury zorientowane na usługi, które kładą szczególny nacisk na interfejsy.
Interfejsy mogą być zorientowane na procedury (takie jak zdalne wywołania funkcji) lub na dokumenty (serwisy internetowe i integracje z protokołem RESTful API). Autor opisuje także wzorce i metody projektowania bazujące na luźnych powiązaniach, które są kluczowe dla budowania systemów rozproszonych. Więcej o interfejsach zorientowanych na dokumenty w moich projektach,
Ważna rzecz, na którą autor także zwraca szczególną uwagę: dziedziczenie jest często trudną do osiągnięcia techniką, jest to także często jedna z najbardziej nadużywanych cech języków obiektowych. Nacisk autora na interfejsy może wydawać się nieco ekstremalny, jednak (świeże w 2006 roku) spojrzenie na projektowanie architektury zorientowane na komponenty i ich odpowiedzialność, daje zaskakująco dobre efekty, a pamiętajmy, że generalnie liczy się cykl życia aplikacji (patrz także artykuł Znaczenie na cykl życia…) czyli koszty jej utrzymania i rozwoju, a nie samo jej wytworzenie. Warto pamiętać, że dziedziczenie z zasady łamie zasadę hermetyzacji i zalecane jest zastępowane go stosowaniem szablonów lub po prostu rezygnuje się z tej techniki na rzecz typów danych i kompozycji.
Podsumowując: Kluczowym przesłaniem autora jest „odejście” od „programowania obiektowego” (orientacja kodu na dziedziczenie oraz łączenie funkcji i danych w obiekty, OOP) na rzecz „projektowania zorientowanego na niezależne, luźno powiązane komponenty” (polimorfizm i hermetyzacja), cechującego się pełną separacją komponentów, luźnymi powiązaniami (tylko wywołania operacji) i pojedynczą odpowiedzialnością (OOAD).
Autor zwraca uwagę na sprawdzone wzorce, kluczowe: to fabryka (zwana także metodą wytwórczą, jest to separacja metod tworzenia obiektów od ich utrwalania), adapter (separacja współpracujących komponentów o niedopasowanych interfejsach). Co do zasady też (wzorce) separujemy przetwarzanie danych od ich samych (wzorzec repozytorium ). Dzisiaj dominujące są więc mikro serwisy i mikro aplikacje, natomiast łączenie danych i metod je przetwarzających w jednej klasie to antywzorzec.
Początek lat 2000 to nie tylko manifest Agile, to także już kilka lat po nim, nawoływanie sygnatariuszy Agile do porządku w inżynierii oprogramowania . Poza omawianą tu książką pojawiły się, w tamtym okresie, między innymi opis budowy komponentowej architektury , opis projektowania zorientowanego na odpowiedzialność komponentów .
Autor zwraca także szczególną uwagę na dokumentowe modele budowy interfejsów i integracji. Dokumentowe czyli zorientowane na przekazywanie między komponentami całych agregatów danych (zwanych dokumentami) zamiast wyników działania poszczególnych funkcji. Znakomicie upraszcza to architekturę, powoduje mniejsze uzależnienia, w konsekwencji cykl życia takiego systemu jest znacznie tańszy. O tym aspekcie architektury integracji pisał także znany autor Martin Fowfer .
Zachęcam do lektury tej książki, porządkuje wiedzę, być może wielu z Was znajdzie coś nowego. Od siebie powiem, że podejście takie stosuję od czasów lektury Systemów zorientowanych komponentowe Souzy, czyli od ponad 15 lat…
A architekturze
Doskonałym i aktualnym uzupełnieniem tej książki jest napisana później Czysta architektura :
Dobrzy architekci ostrożnie oddzielają szczegóły od reguł biznesowych, robiąc to tak dokładnie, że reguły te nie mają żadnej wiedzy o szczegółach i w żaden sposób od nich nie zależą. Dobrzy architekci tak projektują reguły systemu, żeby wszystkie decyzje dotyczące szczegółów można było podejmować tak późno, jak to tylko możliwe.
Przy tej okazji polecam także prezentację opartą na treści książki , szczególnie część o interfejsach, głębokości i płytkości klas: A Philosophy of Software Design | John Ousterhout | Talks at Google
OOP i OOAD czyli co dalej?
[2023-01-07]
Od wielu lat obserwuję rynek i projekty z zakresu inżynierii oprogramowania. Pojęcia OOP (programowanie obiektowe) oraz OOAD (analiza obiektowa i projektowanie) oddalają się od siebie. Techniki organizacji kodu rodem z C++/Java (mają współny rodowód) zdeterminowały pojmowanie pojęcia „programowania obiektowego”. Są to ciężkie metody pracy, oparte na starych wodospadowych założeniach (monolityczna architektura oparta na relacyjnym modelu danych) sprawdzają się tam, gdzie zmienność wymagań jest mała.
C++ znajduje zastosowanie w tworzeniu systemów operacyjnych (np. Windows XP czy Vista), a także podczas budowy aplikacji desktopowych (pakietu Office bądź produktów Adobe). Z wykorzystaniem C++ można spotkać się także podczas budowy baz danych oraz serwerów. Popularność C++ zdecydowanie nie słabnie wśród twórców gier. Sprawdza się świetnie nie tylko podczas produkcji prostych projektów 2D, ale także gier typu AAA.
Język Java stosuje się przede wszystkim w backendowej części budowy internetowych aplikacji. Wykorzystuje się go także w projektowaniu aplikacji desktopowych, korporacyjnych, a nawet całych serwerów aplikacji. Język Java stanowi podstawę działania aplikacji mobilnych oraz gier dla systemu Android. Java stosowana jest także w systemach bankowych i giełdowych.
źr.: https://work4.dev/blog/28/Czy-C-i-Java-sa-do-siebie-podobne.html
Systemy określane jako „biznesowe” to zupełnie inna klasa oprogramowania, to aplikacje budowane w oparciu o reguły biznesowe i odrębne dokumenty. Jedne i drugie szybko sią zmieniają, stosowaniu tu metod i narzędzi adekwatnych do budowy gier i systemów operacyjnych (one się zmieniają rzadko i nie służą do zarządzania danymi) prowadzi to do powstawania bardzo kosztownych w utrzymaniu i rozwoju systemów. Dlatego równolegle rozwijają się takie języki jak JavaScript, Ruby, PHP, Pyton, HTML czy Perl.
Analiza i projektowanie „obiektowe”, pierwotnie oparte na idei hermetyzacji, luźnych powiązaniach i interfejsach, tak na prawdę sprowadza się do poprzedzania kodowania projektowaniem architektury, a to „tylko” komponenty i ich współpraca, bez wnikania w technologie ich powstania, tym bardziej, że wiele z nich (komponenty) można kupić jako COTS (ang. Commercial off-the-shelf, gotowe komponenty z półki) bez wiedzy o ich wewnętrznej strukturze (czyli hermetyzacja).
Developerzy często posługują sie pojęciem klasy mając na myśli konstrukcje znane im z C++ czy Java. Poniekąd słusznie, bo faktycznie ich używają tworząc implementacją (pisząc kod). Na etapie analiz i projektowania detale kodu nie mają znaczenia, liczy się realizowana logika i architektura.
A gdzie tu UML? Mityczne generowanie kodu z UML nie wyszło poza mury akademickich entuzjastów tego pomysłu. Więc gdzie? Po oczyszczeniu z nadmiaru (redukcja UML), jest to doskonałe narzędzie do modelowania systemów i tworzenia sformalizowanych schematów blokowych. Czym jest klasa w UML? Wszystkim, a to co jest klasą w C++/Java to malutka część tego „wszystkiego”. Czy na etapie projektowania (model PIM) mamy na myśli klasy w rozumieniu konstrukcji kodu C++/Java? Nie, na tym etapie mamy komponenty (ale w UML wszystko jest klasą, komponent też), w zasadzie czarne skrzynki z interfejsami, które trzeba (wystarczy) opisać. To co opisze projektant zależy od niego: tam gdzie uzna, że daje swobodę deweloperowi poprzestanie na komponentach, ich interfejsach i procedurach (algorytmach) realizowanych przez te komponenty. Tam gdzie uzna, że to ważne, narzuca wybrane szczegóły (patrz Kto jest programistą).
Dlatego od bardzo dawna (patrz opisywana wyżej książka) mówi się i pisze, że projektowanie systemów to właśnie projektowanie zorientowane na komponenty i ich interfejsy. Implementacja jest zawsze wtórna. A to co można nadal spotkać w wielu podręcznikach i analizach pod nazwą „diagram klas”, to często poprawne i zarazem bezwartościowe diagramy w UML (Patrz UML dla programistów Java).
Na zakończenie ciekawa prezentacja: najpierw projektowanie, kod na końcu.
Bardzo ciekawy artykuł o komponentach i konsekwencji niestosowania wzorców:
https://code.pieces.app/blog/how-to-refactor-large-react-components