Wzorce projektowe (design patterns) to w założeniu ogólne modele związków pomiędzy klasami (i samych klas), jakie mogą pojawiać się w projektach. Każdy taki wzorzec jest przeznaczony do dość ściśle określonych okoliczności, dokładnie opisany i, przede wszystkim, nazwany. Na pewno każdy średnio zaawansowany programista miał okazję spotkać się z takimi terminami jak Iterator, Fabryka czy Singleton. Są one już na tyle długo używane, że w większości przypadków nie ma problemów ze zrozumieniem tego, co w danej sytuacji oznaczają.
Wzorce nie są oczywiście doskonałe. Ze względu na względnie dużą ścisłość nie mogą opisywać wszystkich rozwiązań, jakie mogą być konieczne, jeśli myślimy o zaprojektowaniu dowolnego programu. Nie jest więc tak, że każda klasa, jaka przyjdzie nam głowy, może być od razu wpasowana w jakiś gotowy szablon.
Przeglądając gotowe kody i różnego rodzaju dokumentacje stwierdziłem jednak, że często powtarzają się w nich różnego rodzaju “pseudowzorce”. Objawiają się one głównie używaniem pewnych słów w nazwach klas, dzięki którym można mniej więcej domyślić się, jaka jest rola poszczególnych typów, do czego służą, jak – z grubsza – działają oraz jakie wykazują zależności z innymi klasami. Naturalnie mogą występować spore różnice pomiędzy poszczególnymi bibliotekami i językami, ale przynajmniej dla dwóch największych zbiorów klas, jakie są obecnie w powszechnym użyciu (czyli .NET Framework i JDK), rozbieżności nie są zbyt duże. Co więcej, ponieważ wielu programistów używa któregoś z tych dwóch narzędzi, często przejmują oni te wzorce nazewnictwa (świadomie lub nie) i stosują je we własnych kodach. Kto wie, może dzięki temu przeciętna czytelność kodu produkowanego przez statystycznego programistę (jeśli w ogóle istnieje ktoś taki :]) ma szansę choć odrobinę wzrosnąć?…
Jakie są więc te nieformalne “wzorce”? Otóż znalazłem kilka następujących:
Prawdopodobnie dałoby się wyróżnić jeszcze kilka pozycji (chociaż część byłaby dokładnym odpowiednikiem klasycznych wzorców projektowych), ale, jak widać, w sumie chyba nie jest ich zbyt dużo. To w gruncie rzeczy całkiem dobra wiadomość, gdyż nietrudno zauważyć, że wszystkie te nazwy są raczej “magiczne” i na pierwszy rzut nie przywołują jakichś natychmiastowych skojarzeń – zwłaszcza, jeśli nie jesteśmy do nich przyzwyczajeni. Ale taki już urok projektowania zorientowanego obiektowo, polegającego na tworzeniu dziwnych bytów i jeszcze dziwniejszych zależności między nimi :)
Możesz powiedzieć trochę więcej o Providerze i Context’cie? (jak to napisac? :P). Jak dotąd używałem tylko Managerów i Handlerów :P
Chętnie poznam nowe formy kształtowania kodu ^^
W myśl zasady:
“O ile Ty staniesz się lepszy – Twój kod stanie się lepszy” :P
Provider to abstrakcyjny interfejs, który implementuje się, aby inna klasa lub funkcja mogła wykonywać pewne operacje w sposób dla nas pożądany. Przykład to choćby IFormatProvider z .NETa, który to odpowiada za sposób interpretacji znaczników formatowania tekstu (coś jak printf() z C++).
Context nazwałem ‘wzorcem’ może trochę na wyrost. Generalnie jest to taki obiekt narzędziowy, który jest dostępny właściwie wszędzie (może być globalny), który udostępnia jakąś kluczową funkcjonalność. Przykład to HttpContext z ASP.NET, za pośrednictwem którego ma się dostęp do żądania HTTP, strumienia odpowiedzi na to żądanie, danych serwera, danych sesji, danych zalogowanego usera – czyli dosłownie wszystkiego, co jest potrzebne aplikacji webowej do działania.
Mój własny przykład to MathContext – obiekt główny parsera wyrażeń matematycznych, który potrafi tworzyć obiekty rzeczonych wyrażeń, ale także pamięta wartości zmiennych oraz definicje niestandardowych funkcji, które mogą być wykorzystywane przez te wyrażenia.
Nie nazywałbym “Manager’a” wzorcem nieformalnym. Jego istnienie jest jak najbardziej udokumentowane, nawet poświęcono mu osobny dział teorii DP – wzorce z zarządzaniu zasobami (polecam książkę o zbliżonym tytule).
Listener jest także nazywany Observer’em.