Czas na chwilę porzucić Ember i zgłębić trochę techniki programowania. Jeżeli ktoś spytałby mnie o jeden z głównych elementów programowania, odpowiedziałbym – instrukcje warunkowe. Nie będę jednak opisywał czym jest if czy switch. Dzisiaj zajmę się wzorcem projektowym, który może zastąpić wszelkiego rodzaju „ify” i „switche” – wzorcu strategii. Czym jest wzorzec projektowy? Wzorce projektowe są nieodłącznym elementem dobrego programisty – pozwalają pisać kod czystszy i łatwiejszy do zrozumienia przez innych. Pozwalają utrzymać pewien abstrakcyjny zbiór rozwiązań przy abstrakcyjnych problemach. Trzeba jednak pamiętać, że wzorce projektowe nie są gotowymi rozwiązaniami. Oznacza to, że nie są gotowym kodem, który wystarczy skopiować i wkleić. Stanowią one raczej użyteczną i najlepszą praktykę do rozwiązywania problemu z pewnej kategorii.
Przejdźmy jednak do sedna dzisiejszego wpisu, czyli do wzorca strategii. Z założenia wzorzec strategii ma upraszać wszelkiego rodzaju instrukcję warunkowe. Umożliwia on wybór odpowiedniego algorytmu na etapie działania aplikacji.

W języku Javascript jest niezwykle łatwo zastosować wzorzec strategii by np. ominąć korzystanie z instrukcji warunkowej switch. Poniżej dwa przykłady obiektu calc. Obiekt będzie posiadał tylko jedną metodę count przyjmującą trzy argument – liczbę a, liczbę b i typ działania matematycznego. Łatwo się domyślić, że metoda będzie zwracała wynik tego działania.
Przykład bez wykorzystania wzorca strategii – instrukcja warunkowa switch

 

 

var calc = (function() {
  var me = {
    count : count
  };

  function count(type, a , b) {
    var result;
    switch(type) {
      // dodawanie
      case 'add':
        result = a + b;
      break;
      // odejmowanie
      case 'sub':
        result = a - b;
      break;
      // mnożenie
      case 'multiplications':
        result = a * b;
      break;
      // dzielenie
      case 'division':
        result = a / b;
      break;
      // brak dział
      default:
        result = 0;
        console.log('Brak instrukcji dla działania: ' + type);
      break;
    }
    return result;
  }

  // zwracamy obiekt 'me'
  return me;
}());


console.log(calc.count('add', 2, 3)); // 5
console.log(calc.count('sub', 6, 3)); // 3
console.log(calc.count('multiplications', 4, 7)); // 28
console.log(calc.count('division', 10, 2)); // 5

 

 

A teraz ten sam obiekt ale z wykorzystaniem wzorca strategii:

 

 

var calc = (function() {
  var me = {
    count : count
  },
  // obiekt pomocniczy przechowujący implementacja
  // wszystkich działań matematycznych
  math_operation = {};

  // implementacja działań matematycznych
  math_operation.add = function(a, b) {
    return a + b;
  };
  math_operation.sub = function(a, b) {
    return a - b;
  };
  math_operation.multiplications = function(a, b) {
    return a * b;
  };
  math_operation.division = function(a, b) {
    return a / b;
  };

  // implementacja metody count
  function count(type, a ,b) {
    // sprawdź czy podana strategia istnieje
    if(!math_operation[type]) {
       console.log('Brak instrukcji dla działania: ' + type);
       return 0;
    }
    // wybór strategii liczenia na podstawie typu    
    return math_operation[type](a, b);
  }

  // zwracamy obiekt 'me'
  return me;
}());


console.log(calc.count('add', 2, 3)); // 5
console.log(calc.count('sub', 6, 3)); // 3
console.log(calc.count('multiplications', 4, 7)); // 28
console.log(calc.count('division', 10, 2)); // 5

 

 

 

Obiekt calc jest raczej mało użyteczny 🙂 ale sądzę, że udało mi się wyjaśnić na jego podstawie implementację prostego wzorca strategii. Jak widać, w drugim przykładzie, metody do wyliczania są nie dostępne na zewnątrz obiektu (kapsułkowane). Obiekt dla wszystkich wyliczeń ma wspólny interfejs (w tym przypadku jedną metodę 🙂 ). Dodatkowo w przypadku dodania kolejnej strategii wyliczenia wystarczy ją dodać do obiektu math_operaion bez konieczności zmiany w kodzie metody interfejsu obiektu calc.

 

jQuery.validateMe()

 

Zagłębiając się w książkę Stoyana Stefanowa – „JavaScript Wzorce” (polecam!) spodobał mi się sposób wykorzystania wzorca strategii do walidacji danych formularza. Osobiście nie lubię pisać walidacji dla formularzy. Postanowiłem zatem wykorzystać przykład z książki, troszkę zmodyfikować i wykorzystać do napisania pluginu jQuery służącego do walidacji danych. Plugin oczywiście jest dostępny na moim githubie – tutaj.
Nie będę opisywał całości tworzenia pluginu (być może kiedy indziej) wyjaśnię jednak krótko jego działanie.
Przykładowo mamy formularz zawierający 4 elementy, które chcemy odpowiednio walidować:
– pole imie – nie może zawierać znaków specjalnych i pole nie może być puste
– pole naziwsko – nie może zawierać znaków specjalnych i nie może być pustę
– pole wiadomosc – musi zawierać minimum 10 znaków
– pole email – musi być poprawnym adresem email.

W kodzie pluginu dla każdej z powyższych walidacji jest napisana odpowiednia metoda. Przy wywołaniu metody validateMe() obiektu formularza, należy wskazać jaki typ sprawdzenia powinien zostać wykorzystany dla każdego z jego elementów. Dodatkowo metoda przyjmuję również callback, który będzie wywoływany dla każdego z pól, które nie przejdzie walidacji. Poniżej przykład:

 

 

$(document).ready(function() {
  $form = $('#example-form');

  $form.on('submit', function() {
    return $(this).validateMe({
      assertions : {
        first_name : ['isNonEmpty', 'isAlphaNum'],
        last_name  : ['isNonEmpty', 'isAlphaNum'],
        message    : ['lengthIsGreaterThan-10'],
       email      : ['isEmail']
      },
      onAssertionFail : function($field, msg) {
        if(!$field.hasClass('hasError')) {
          // dodaj klasę 'hasError' dla każdego elementu formularza,
          // który nie przejdzie poprawnie walidacji
          $field.addClass('hasError');
        }
      }
    });
  });
});

 

 

Na dzisiaj to wszystko. Trochę skromnie. Nie jest to jednak koniec wpisów dotyczących wzorców projektowych. Następnym razem wzorzec Obserwator.