Komentarz W poszukiwaniu zaginionego wzorca
W poszukiwaniu zaginionego wzorca Administrator nadal z powodzeniem używa tego samego programu do zarządzania usługami. Aczkolwiek pojawia się coraz więcej systemów oraz różnorodnych usług. Z tego względu zarządzanie zmianami w programie staje się coraz bardziej uporczywe i nieprzewidywalne. Szczęśliwie pojawiło się kilku poszukiwaczy, którzy odnaleźli wzorce pozwalające na radykalne zmiany w tej materii. Miejmy nadzieje, że inżynierowie szybko wprowadzą poprawki. Dzięki za udział w poszukiwaniach! Co było do zrobienia? Drugie zadanie przedstawiało zagadnienie dotyczące zarządzania zmianami w kodzie źródłowym programu oraz związanym z tym nieustannym procesem refaktoryzacji. Głównym zadaniem uczestników było zlokalizowanie w kodzie newralgicznego fragmentu, który powodował największe niedogodności przy wprowadzaniu obsługi instalacji usług na nowych systemach. Następnie należało wyodrębnić zaproponować (najlepiej z uzasadnieniem) - wzorzec projektowy, który usuwał te niedogodności. Dodatkowo zadanie umożliwiało refaktoryzację jako potwierdzenie własnej tezy. W trakcie refaktoryzacji można było użyć dowolnej techniki, jednak tak aby zachować istniejącą funkcjonalność. Było również możliwe dobieranie dodatkowych wzorców. Każdy dodatkowy wzorzec, rzecz jasna, rozsądnie zaaplikowany pozwalał na zwiększenie punktacji. Rozwiązanie Głównym problemem przy wprowadzaniu nowych usług i installer'ów był rozrastający się kod w klasie ServiceInstaller oraz narastająca lawinowo ilość kolejnych installer'ów. W ServiceInstaller należało zamienić blok 'if...else' na generyczną pętlę, która nie ulegała by modyfikacji przy wprowadzaniu nowych elementów. Natomiast installer'y należałoby ukryć pod warstwą abstrakcji. Zdecydowanie najlepiej w takiej sytuacji pasuje wzorzec Visitor, gdzie visitorami są poszczególne installer'y natomiast komponenty to elementy akceptujące visitora. Podpowiedzią był fakt, że na każdym komponencie serwisu wywoływana jest taka sama operacja - install, zaś konkretna implementacja jest wybierana w trakcie wykonywania programu. Dzięki temu unikamy bezpośredniej zależności komponent - installer, a zyskujemy dowolność w dobieraniu installerów mający wykonać operację instalacji. Innym wzorcem podobnie realizującym ten schemat jest wzorzec Bridge. Przy czym należałoby uzyskać więcej szczegółów na tematu rozwoju programu, aby dokonać wyboru pomiędzy Bridge a Visitor.
Jak ocenialiśmy Oceniając zadanie posługiwaliśmy się trzema kryteriami: 1. Trafność zaproponowanego wzorca projektowego (maksymalnie 50pkt.) Punktacja wzorców: Visitor 40pkt. Bridge 30pkt. Strategy 10pkt. Mediator 10pkt. Command 8pkt. Builder, Abstract Factory, Factory 4pkt Dodatkową punktacją były objęte odpowiedzi wraz z próbą uzasadnienia lub przekonującym uzasadnieniem: przekonujące uzasadnienie 10pkt. próba uzasadnienia 5pkt. 2. Refaktoryzacja (maksymalnie 80pkt.) Refaktoryzacja oryginalnego kodu była potwierdzeniem tezy stawianej w punkcie pierwszym. Dlatego punktami były objęte: zmiany w kodzie przeprowadzone zgodnie z założeniami Najwięcej otrzymywały rozwiązania ze wzorcem Visitor (60pkt.). Następnie Bridge(40pkt.). Pozostałe wzorce były punktowane do 20pkt. rozsądne zastosowanie dodatkowych wzorców. Za każdą dodatkową refaktoryzację po 10pkt. łatwość wprowadzania nowych elementów po refaktoryzacji. 10pkt. próby minimalizacji ilości klas dla nowych elementów. 1pkt. zachowanie istniejącej funkcjonalności. 10pkt. 3. Punkty extra (maksymalnie 20pkt.) Za wzorcowe, nietypowe, ponadprzeciętne rozwiązania.
Jak poszło? Wszystkich nadesłanych odpowiedzi 35 Najwięcej uzyskanych punktów 140 Najmniej uzyskanych punktów 0 Suma punktów ze wszystkich odpowiedzi 1187 Średnia arytmetyczna 33.91 Większość nadesłanych rozwiązań była zgodna z zadeklarowanym wzorcem projektowym. Niestety pojawiło się tylko kilka, które naprawdę usuwały niedogodności w oryginalnym kodzie. Były to najwyżej punktowane rozwiązania z refaktoryzacją do Visitor lub Bridge. Autorzy tych odpowiedzi wykazali się zrozumieniem zagadnienia oraz dobrą, praktyczną znajomością wzorców. Pozostałe najczęściej typowane to wzorce kreacyjne, tj. Abstract Factory, Builder, Factory Method, Factory nie rozwiązywały one jednak problemu, a z reguły przesuwały go w inne miejsce kodu. Wnioski Najbardziej istotnym elementem w procesie refaktoryzacji jest właściwe rozpoznanie schematu według którego obiekty komunikują się ze sobą. Jest to sztuka wymagająca wprawy, doświadczenia i dokładnej znajomości wzorców. Niewłaściwe dopasowanie wzorca do problematyki może doprowadzić do większej komplikacji i bardziej nieczytelnego kodu. Późniejsze modyfikacje mogą okazać się zmorą. Niestety większość uczestników nietrafnie wytypowała schemat. Co w efekcie dało kod nadal operujący na badaniu typu obiektu (np. instanceof w Java) z tym, że przesunięty do innej klasy i dodatkowo obłożony wyjątkami i rzutowaniem. Liczymy na to, że treść zadania nakłoni do zgłębienia tematu wzorców projektowych. Dodatkowo podkreślamy, że poza najczęściej stosowanymi wzorcami kreacyjnymi istnieją również inne wzorce. Między innymi behawioralne (np. Visitor) i strukturalne (np. Bridge). Zachęcamy do zapoznania się z nimi oraz częstych ćwiczeń. Pamiętajcie - programowanie to rzemiosło. Bez treningu nie da rady osiągnąć poziomu Master
Ciekawostki W jednym z rozwiązań pojawiła się pętla w zależnościach. Kod w C++. Pojawiały się próby ukrycia niezmienionego bloku 'if...else' z ServiceInstaller w innych klasach. 'instanceof' w ServiceInstaller zastąpiony przez Class.forName(className).newInstance() + rzutowanie. Kod w Java. Dostaliśmy rozwiązanie zadania z repozytorium git. A wraz z nim cała historia zmian. Bardzo fajnie. Pamiętajcie tylko, że jeżeli pracujecie w rządowych lub wojskowych instytucjach nie powtarzajcie tego! Najlepsze przykłady refaktoryzacji zaproponowali dwaj programiści C++ (do Visitor) oraz jeden Java (do Bridge). Były to jedyne rozwiązania całkowicie i świadomie rozwiązujące problem.