_125x125.jpg)
Postem tym chciałbym rozpocząć mini cykl o zastosowaniu wzorców projektowych w języku JavaScript - wbrew pozorom, w tym języku również można je stosować! Na pierwszy ogień przedstawię dziś wzorzec Strategia w JavaScript. Myślę, że większość z nas, stosowała i stosuje ten wzorzec na co dzień (czasem nawet nieświadomie), jednak dla przypomnienia (za wikipedią):
.jpg)
Wzorzec Strategia w JavaScript
Postem tym chciałbym rozpocząć mini cykl o zastosowaniu wzorców projektowych w języku JavaScript - wbrew pozorom, w tym języku również można je stosować! Na pierwszy ogień przedstawię dziś wzorzec Strategia w JavaScript. Myślę, że większość z nas, stosowała i stosuje ten wzorzec na co dzień (czasem nawet nieświadomie), jednak dla przypomnienia (za wikipedią):
Wzorzec strategii definiuje rodzinę algorytmów, z których każdy zdefiniowany jest w osobnej klasie implementującej wspólny interfejs - dzięki temu możliwe jest wymienne stosowanie każdego z tych algorytmów, nie będąc zależnymi od klas klienckich.
Przykład - HTML
To tyle tytułem wstępu - zakładam, że implementacja tego wzorca w języku takim jak np. C# jest znana, chciałbym się więc skupić na tym jak skorzystać z niego JavaScript. Dla osób, które nie miały dotąd styczności z OOP w JavaScript, proponuje wcześniejsze zapoznanie się z tą tematyką. Można zacząć na przykład od stron mozilli dla developerów. Przejdźmy zatem do przykładu - załóżmy taki oto kod html:
<fieldset>
<label for="error">Błąd</label>
<input type="radio" name="selection" value="error" />
<label for="warnirng">Ostrzeżenie</label>
<input type="radio" name="selection" value="warning" />
<label for="info">Informacja</label>
<input type="radio" name="selection" value="info" /><
</fieldset>
<input type="button" value="Wyswietl" />
Mamy tutaj trzy radiobuttony, przycisk_‘Wyświetl’_ oraz paragraf. Radiobuttony odpowiadają opcjom_‘Błąd’,‘Ostrzeżenie’_ oraz ‘Informacja’ - po wybraniu jednego z nich i naciśnięciu przycisku ‘Wyświetl’, wewnątrz tag’u <p>, zamiast wartości ‘—’ ma się pojawić odpowiednia informacja (dodatkowo w zależności czy będzie to błąd, ostrzeżenie czy informacja, tekst powinien być odpowiednio “ostylowany”).
Obsługa zadania
Do obsługi takiego zadania moglibyśmy użyć takiego kodu JavaScript:
$(document).ready(function() {
$('input:radio').change(function() {
var checkedVal = $(this).val();
switch (checkedVal) {
case 'error':
$('input[type="button"]').click(function() {
$('p').attr('class', '')
.addClass('error')
.text('Błąd');
});
break;
case 'warning':
$('input[type="button"]').click(function() {
$('p').attr('class', '')
.addClass('warning')
.text('Ostrzeżenie');
});
break;
case 'info':
$('input[type="button"]').click(function() {
$('p').attr('class', '')
.addClass('info')
.text('Informacja');
});
break;
}
});
});
Jak widać, w “on document ready” (jQuery) mamy inicjalizację obsługi zdarzenia change
dla wszystkich radiobuttonów na stronie. Funkcja obsługująca zdarzenie pobiera wartość atrybutu value
radiobuttona, który został w danym momencie kliknięty - w zależności od tej wartości, definiowana jest funkcja obsługująca zdarzenie click
przycisku (tego, który wyświetlać ma komunikat). W powyższym przykładzie, zastosowana została instrukcja warunkowa switch
, a każdy z obsługiwanych przypadków jest bardzo podobny - mamy funkcję obsługującą zdarzenie click
, a w niej, z pomocą metod jQuery, odpowiednie “ostylowanie” elementu <p> i nadanie odpowiedniego tekstu.
Myślę, że w takim przypadku, aż się prosi o zastosowanie wzorca strategii… W naszym przypadku, utworzymy klasę abstrakcyjną zawierającą deklarację metody show()
, która odpowiedzialna będzie za odpowiednie “ostylowanie” naszego elementu <p\>
- odpowiednia implementacja tej metody będzie wywoływana w funkcji obsługującej zdarzenie click buttona Wyświetl
.
Implementacja strategii
Zacznijmy więc przykład implementacji strategii, na początek klasa bazowa:
var Message = function() {}; // define abstract class
// declare abstract function
Message.prototype.show = function() {
throw 'Implement abstract class!!';
};
Mamy tutaj definicję klasy Message
, z konstruktorem bez parametrów (function()
nie przyjmuje żadnych parametrów). Ponadto definiujemy metodę show()
, która rzuca wyjątkiem jeśli próbowalibyśmy użyć jej bezpośrednio (czyli jest to metoda bardziej wirtualna niż abstrakcyjna - w JavaScript nie mamy takiego rozróżnienia, liczy się efekt jaki w ten sposób osiągamy - jeśli w klasie dziedziczącej po Message
nie zaimplementujemy metody show()
, rzucony zostanie wyjątek).
Klasy dziedziczące po Message
Kolejna rzecz, to implementacja poszczególnych klas strategii dziedziczących po Message. Poniżej jedna z takich klas:
var ErrorMessage = function() { /*parameterless constructor*/ };
ErrorMessage.prototype = new Message; // extend of abstract class
ErrorMessage.prototype.show = function() {
// first algorithm implementation
$('p').attr('class', '')
.addClass('error')
.text('Błąd!');
};
Na powyższym przykładzie, widać w jaki sposób realizowane jest dziedziczenie w JavaScript - w pierwszej linijce mamy deklarację klasy ErrorMessage
za pomocą konstruktora bezparametrowego. W kolejnej linii informujemy, że klasa ErrorMessage
dziedziczy z Message
(klasa Message
jest prototypem dla ErrorMessage
). Kolejne linijki to już konkretna implementacja/przesłonięcie metody show()
. Poniżej pozostałe strategie:
var WarningMessage = function() { /*parameterless constructor*/ };
WarningMessage.prototype = new Message; // extend of abstract class
WarningMessage.prototype.show = function() {
// first algorithm implementation
$('p').attr('class', '')
.addClass('warning')
.text('Ostrzeżenie');
};
var InfoMessage = function() { /*parameterless constructor*/ };
InfoMessage.prototype = new Message; // extend of abstract class
InfoMessage.prototype.show = function() {
// first algorithm implementation
$('p').attr('class', '')
.addClass('info')
.text('Informacja');
};
Wykorzystanie strategii w praktyce
Wzorzec strategii, w codziennym zastosowaniu bardzo często występuje w połączeniu z wzorcem fabryki - np. klasy ze statycznymi metodami zwracającymi odpowiednie implementacje algorytmu. W naszym przypadku zrobimy coś podobnego - zdefiniujemy obiekt, z właściwościami, których nazwy odpowiadać będą wartościom (atrybuty value
) poszczególnych radiobuttonów, a wartości przypisanym im strategiom:
var messages = {
error: new ErrorMessage(),
warning: new WarningMessage(),
info: new InfoMessage()
};
Pozostało nam już tylko wykorzystanie tak utworzonych strategii w praktyce:
$(document).ready(function() {
$('input:radio').change(function() {
currentMessage = messages[$(this).val()];
$('input[type="button"]').click(currentMessage.show);
});
});
Tak jak w przykładzie na początku artykułu, mamy obsługę zdarzenia change
radiobuttonów, z tą różnicą, że zamiast instrukcji warunkowej switch
wykorzystujemy utworzone wcześniej strategie - jak widać w linii nr 3, na podstawie wartości aktualnie zaznaczonego radiobuttona, z obiektu messages
, pobierana jest odpowiednia klasa strategii. Następnie metoda show()
tej klasy, wykorzystywana jest do obsługi zdarzenia click
przycisku Wyświetl
.
W ten sposób uniezależniliśmy wyświetlanie komunikatów od funkcji obsługującej zdarzenie click
- jeśli chcemy dokonać zmian w sposobie wyświetlania, robimy to w odpowiednich klasach strategii. Możemy również napisać nowe strategie wyświetlania komunikatów i podmienić je - wszystkie korzyści płynące z wzorca strategia są dostępne.
Wzorzec strategia w JavaScript - podsumowanie
To w zasadzie wszystko, jak widać skorzystanie z benefitów wzorców projektowych jest możliwe również w języku JavaScript. Mam nadzieję, że teraz wzorzec strategia w JavaScript nie będzie już dla Ciebie tajemnicą… W przyszłości postaram się pokazać również implementację innych wzorców, znanych nam z typowych języków obiektowych.
W pełni działający opisany powyżej dostępny jest do przetestowania w jsfiddle.