Bardzo trudno jest napisać fragment kodu, o którym moglibyśmy powiedzieć, że jest idealny. Praktycznie niemożliwe jest zrobienie takiej rzeczy za pier

Bardzo trudno jest napisać fragment kodu, o którym moglibyśmy powiedzieć, że jest idealny. Praktycznie niemożliwe jest zrobienie takiej rzeczy za pierwszym razem. Przez pierwszych kilka lat w zawodzie programistom często zdarza się wracać do wcześniej napisanego kodu z myślą „co autor miał na myśli?”. Rozpacz jest tym większa, gdy okazuje się, że sami są jego autorami. Dlatego cały czas warto się rozwijać i ćwiczyć pisanie pięknego i idealnego kodu, nawet jeśli niekiedy będzie on wymagać gruntownej refaktoryzacji. Ta ostatnia niestety nie zawsze jest możliwa. Jeśli stworzyliśmy klasę, której kod jest używany również poza naszym projektem, to bardzo szybko się okazuje, że nie możemy sobie pozwolić na swobodne modyfikacje, bo łatwo możemy doprowadzić do tak dramatycznych sytuacji jak TA po usunięciu 11-liniowej biblioteki z repozytorium npm. Dlatego, jeśli tylko nasz kod żyje, każda zmiana musi być wprowadzana z głową. Oczywiście to ogranicza nam pole manewru, ale od czego są wzorce projektowe? Tytułowy adapter pozwala nam poradzić sobie z sytuacją, w której chcielibyśmy poprawić interfejs naszej klasy, ale nie możemy tego łatwo zrobić ze względu na istniejące zależności.   Wzorzec adapter Wzorzec adapter jest stosunkowo prosty; sprawdzi się wszędzie tam, gdzie potrzebna jest modyfikacja istniejącego interfejsu klasy, ale nie do końca jest to możliwe ze względu na związane z taką operacją implikacje. Kluczowymi elementami tego wzorca są interfejs i klasa adaptera. Ta druga opakowuje istniejące implementacje wewnątrz metod zdefiniowanych we wspomnianym interfejsie. W luźnej terminologii można by więc określić adapter jako swego rodzaju „przelotkę”, która pod warstwą nowych sygnatur ukrywa sprawdzony kod. W pewnych sytuacjach adapter może więc być wzorcem przejściowym. Adaptowana klasa może wciąż funkcjonować w naszym rozwiązaniu, ale jako rozwiązanie obsolete. Po pewnym czasie możliwe będzie przeniesienie jej zawartości po prostu do wnętrza adaptera. Możliwe jest również permanentne współistnienie obu rozwiązań.   Przykład praktyczny W ramach przykładu przygotowałem prostą, starą i brzydką klasę, która nie miała nawet wystawionego publicznego interfejsu. Ogólnie jest to prosty messenger , który standardowo, na potrzeby artykułu, wypluwa jakiś bezsensowny tekst na konsolę:   [sourcecode language="csharp"] public class MessageManager { public void SendMessage(string info) { Console.WriteLine(info); } } [/sourcecode]   Wyobraźmy sobie, że nasza brzydka klasa została zaimplementowana tu i ówdzie. Po czasie dostrzegliśmy wady tego rozwiązania, a ponadto zmieniły się wymagania niektórych klientów. Na bazie oczekiwań powstał interfejs IMessenger, który spełnia oczekiwania naszych klientów. Jednocześnie stare rozwiązanie wciąż jest aktywne ze względu na to, że systemy działają produkcyjnie. Jest to dla nas znak, że powinniśmy zastosować wzorzec projektowy adapter. Jak przeprowadzić taką operację? Przede wszystkim trzeba zacząć od definicji samego interfejsu:   [sourcecode language="csharp"] public interface IMessenger { void Send(string msg); } [/sourcecode] Jak widać, wciąż chodzi o to samo, ale ponieważ nie możemy zmodyfikować starej klasy, wykorzystamy nasz nowy, piękny wzorzec projektowy. Poniżej implementacja klasy Messenger:   [sourcecode language="csharp"] public class Messenger : IMessenger { private readonly MessageManager _messageManager; public Messenger(MessageManager messageManager) { _messageManager = messageManager; } public void Send(string msg) { // Do some optional stuff _messageManager.SendMessage(msg); } } [/sourcecode]   Klasa Messenger stała się adapterem i rozpoczęła proces „adaptacji” naszej starej klasy do nowego interfejsu. Mówiąc po dewelopersku, stworzyliśmy prosty wrapper. Bonus jest taki, że możemy wstrzyknąć dodatkowy kod do każdej metody wrapującej. Poniżej również testowy kod klasy Program:   [sourcecode language="csharp"] public class Program { public static void Main(string[] args) { IMessenger messenger = new Messenger(new MessageManager()); messenger.Send("Hello world!"); } } [/sourcecode]   Oczywiście nie jest to optymalne rozwiązanie i wszędzie, gdzie to możliwe, powinniśmy raczej zmieniać docelową implementację. Niestety, nie wszystko w życiu jest zawsze takie proste…  

Jerzy Piechowiak

Altcontroldelete.pl

 


 

 Szukasz informacji o C#? Kliknij TU lub zerknij poniżej: