@Shalom:
[o Strategii]
Ja myśle że jest, bo stosuje się ją automatycznie korzystając z dynamicznego polimorfizmu ;]
To stwierdzenie jest tak naciągane, że uznałbym je po prostu za nieprawdziwe ;).
Granice pomiędzy wzorcami projektowymi bywają niekiedy... rozmyte. Jednak żebyśmy mogli mówić o Strategii, muszą być spełnione warunki, które przeważnie nie są spełnione, gdy korzystamy z polimorfizmu, tj. np. traktujemy obiekt tak, jakby był pewnego abstrakcyjnego typu, podczas gdy naprawdę musi być (oczywiście) pewnego konkretnego podtypu.
We wzorcu Strategia mamy do czynienia z trzema artefaktami:
- Osłoniętym algorytmem, zwanym Strategią (to część wzorca Strategia -- charakterystyczna, ale nie jedyna; IMO nawet nie najbardziej charakterystyczna). Przeważnie mamy klasę abstrakcyjną, która rozwiązuje pewien problem i podklasy konkretne, które rozwiązują ten sam problem, ale każda za pomocą innego algorytmu.
- Obiektem bezpośrednio używającym algorytmu. Obiekt nazywamy czasem Kontekstem.
[--- Tu się kończy implementacja samego wzorca Strategia ---]
- Klientami, którzy korzystają z naszego wzorca. Mają odniesienie do Kontekstu. Tworzą konkretne Strategie i przekazują je Kontekstowi.
Co najlepiej pozwala rozpoznać ten wzorzec, wg mnie?
Nie Strategie, tylko Kontekst. Kontekst posiada referencję do Strategii. Referencja jest typu abstrakcyjnego. Z tym rzeczywiście mamy do czynienia w zwykłym polimorfizmie -- przecież to zwykła kompozycja. Obiekt posiada referencję do innego obiektu.
Ale we wzorcu Strategia jest jeszcze jeden bajer: to klienci przekazują Kontekstowi referencję do konkretnej Strategii. Czyli wybierają, jak Kontekst rozwiąże pewien problem -- za pomocą jakiej Strategii (tj. jakiego algorytmu).
Wzorzec Strategia ma jeszcze kilka innych charakterystyk. Zadaniem Strategii jest kapsułkowanie algorytmu. Przeważnie posiada ona dokładnie jedną metodę publiczną, która wykonuje algorytm na danych zapewnionych przez Kontekst.
Same Strategie nie różnią się niczym od innego wzorca: Obiektu reprezentującego metodę. Bo Strategie to są Obiekty reprezentujące metodę.
Bajer wzorca Strategia polega jednak na tym, że klienci mogą wymieniać algorytmy używane przez Kontekst.
Czym to się różni np. od wzorca Stan? W nim też mamy coś w rodzaju Kontekstu (to obiekt posiadający różne stany) i obiekty przypominające Strategie. Tylko te obiekty mają przeważnie więcej niż jedną metodę i są przez "Kontekst" samodzielnie wymieniane zgodnie z przejściami pomiędzy stanami.
Wzorca Strategia używa się w niektórych językach bardzo często. Można powiedzieć, że w JavaScripcie mamy z nim do czynienia na każdym kroku. Wzorzec jest bardzo dobrze wspierany przez konstrukcje języka. W JavaScripcie, funkcje są obiektami i można je tworzyć i np. przypisywać do zmiennych ad hoc, w dowolnym miejscu. Poszczególne Strategie to po prostu funkcje. Np. w takim kodzie:
var tablica = [1, 2, 3, 4]; // to samo co = new Array() + przypisywanie elementów
var kwadraty = tablica.map(function(element) {
return element * element;
});
alert(kwadraty); // 1, 4, 9, 16
Tutaj tablicę
można uznać za Kontekst, funkcję anonimową przekazywaną do .map()
za Strategię, a cały ten wycinek kodu to kod kliencki. Mapujemy elementy tablicy (liczby) wg określonej Strategii (zwracającej kwadraty liczb).
BTW, @Shalom: w komentarzu pisałeś o szablonach jako statycznym polimorfizmie w C++. Akurat w książce Bandy Czterech wspominają o tym, że w ten sposób także można zrealizować Strategię. I też się liczy :).
Ja wzorców używam raczej często. Pracuję nad aplikacjami sterowanymi zdarzeniami, więc wzorzec Obserwator jest w użyciu non stop.
Singleton... w dużej aplikacji jakiś tam się trafi (np. pomaga zarządzać wersjami językowymi), ale nie nadużywam go, bo nie różni się on wiele od zmiennych globalnych i użycie Singletona to dla mnie zawsze lampka ostrzegawcza. Ludzie często jednak wyjeżdżają z Singletonem podczas rozmowy o wzorcach, nawet na rozmowach kwalifikacyjnych. Wydaje mi się, że to bardziej spowodowane jest tym, że wzorzec ten ma wpadającą w ucho nazwę ;D i jest prosty do zapamiętania.
Relatywnie często korzystam z leniwej inicjalizacji, która też jest swoistym wzorcem. Najczęściej piszę w JavaScripcie, który ma dziedziczenie prototypowe, więc automatycznie korzystam też ze wzorca Prototyp.
Metoda wytwórcza -- jak najbardziej. W JavaScripcie nie chodzi w niej jednak o to, żeby zwracała typ abstrakcyjny, a tworzyła konkretne obiekty, bo JS ma i tak bardzo dynamiczną typizację i nikogo nie obchodzi konkretny typ obiektu. Chodzi więc raczej o kapsułkowanie skomplikowanego tworzenia obiektu w jednej funkcji.
Budowniczy... użyłem na pewno w dwóch ostatnich aplikacjach, nad jakimi pracowałem. W tej ostatniej ze trzy, dość wypasione razy. Dwa służyły do zbudowania walidatorów obsługi formularzy. Trzeci... budował drzewo zależności pomiędzy plikami. To były funkcyjne, monadowe API.
Metoda szablonowa -- często. Staram się zawsze trzymać zasady Otwarty-Zamknięty. Piszę małe, niezależne funkcje, z czego jedna wywołuje drugą i chcę umożliwić łatwą wymianę dowolnej z nich.
Adapter zdarza się, gdy chcę dostosować jakieś stare/niewygodne/zmienne API. Kiedyś kumple-programiści z ówczesnego projektu dumali nad tym, jakim cudem chce mi się pisać testy automatyczne i stosować te wszystkie wzorce, podczas gdy pracujemy w dużym projekcie zahaczającym o bloatware i rozkład oprogramowania. Jeden stwierdził, że ja się po prostu okopałem warstwami abstrakcji od całego tego crapu i operowałem we... własnym crapie.
Fasada dość często, gdy chcę zapewnić bardzo proste API dla prostych przypadków. Niedawno trochę się zdziwiłem, gdy podliczyłem, że jedna Fasada, zapewniająca bodajże jedną czy dwie metody (choć ze sporymi możliwościami konfiguracji), korzysta wewnętrznie z już chyba kilkudziesięciu plików z kodem źródłowym, co dla JavaScriptu (bo w tym akurat pisałem) jest już czymś dość skomplikowanym.
Pewnie jeszcze trochę by się tego wszystkiego znalazło. O ile mi wiadomo, wzorców używam świadomie. Staram się też nadawać poszczególnym obiektom odpowiednie nazwy tak, by od razu było widać, że dany wzorzec jest w użyciu. Czyli np. XxxBuilder
dla Budowniczych, a dla Metod wytwórczych createXxx()
.
edit:
Dekoratora w sumie też ostatnio użyłem. Do rodziny okienek typu "popup". Jeden moduł obsługiwał praktycznie całą aplikację webową (poza panelem administracyjnym). Dodatkowe zachowania -- np. keszowanie, walidacja, wysyłanie formularza ajaxem -- dodawało się do okienek dekoratorami.
Z popularnym Model-Widok-Kontroler oprócz przygód dobrych, miałem też dość nieprzyjemną -- w celach testowych trochę go nadużyłem, pisząc tak całą aplikację, która być może nie powinna być tak napisana. To jest mega-wzorzec, zaważył więc na całej architekturze aplikacji. Nie było tragicznie, ale nie było też za dobrze.
Iterator oczywiście jest używany na co dzień. W takiej Javie, z Iterable i wsparciem języka, wszystko jest jasne, ale wydaje mi się, że nawet w JavaScripcie, mimo wszystko, można nazwać popularne kontenery iteratorami wewnętrznymi:
// natywna tablica
var tab = ["a", "b", "c"];
tab.forEach(function(element) {
alert(element);
// jest iteracja? jest
});
// kolekcja jQuery
var $kolekcja = $(".moje-elementy");
$kolekcja.each(function(indeks, element) {
$(element).hide(); // też się iterujemy
});
Na pewno jest to przykład iteracji wewnętrznej, choć zakres odpowiedzialności samych kontenerów jest oczywiście większy niż tylko iteracja. Można jednak powiedzieć, że kontenery te są iteratorami wewnętrznymi po samych sobie ;).