Nie będe się rozpisywał na temat teorii związanej z tym wzorcem, ale opiszę w dwóch zdaniach jego ideę. Adapter służy do zaadaptowania (przystosowania) klas wykorzystujące określone API do współdziałania z klasami wykorzystujące inne API. W świecie rzeczywistym adapter to przejściówka, np. przejściówka do wtyczki gniazdka angielskiego na polskie. W tym artykuje będe budował / pisał przejściówkę z klas wykorzystujących API Zend_Validate_Abstract na klasy wykorzystujące API sfValidatorBase. Więcej o adapterze znajdziecie w google.
Pierwszym krokiem jaki trzeba wykonać to prównanie API.
Na początek API do którego dążymy, czyli to z symfony.
[PHP]
//skrócone api sfValidatorBase class sfValidatorBase { //metody do zarządzania opcjami walidatora public function getOption($name); public function setOption($name, $value); public function addOption($name, $value); public function hasOption($name); public function getOptions(); public function setOptions($values); //metoda walidująca public function clean($value); //metody do zarządzania komunikatami błędów public function getMessage($name); public function addMessage($name, $value); public function setMessage($name, $value); public function getMessages(); public function setMessages(); public function getDefaultMessages(); protected function setDefaultMessages($values); }
API które chcemy przystosować do tego wyżej zaprezentowanego.
[PHP]
class Zend_Validate_Abstract { public function __constructor($arg1, $arg2, $arg3, ...); //metoda walidująca public function isValid($value); //metody do zarządzania komunikatami błędów public function getMessages(); public function getMessageVariables(); public function getMessageTemplates(); public function setMessage($value, $key = null); //metody do zarządzania opcjami (Xxx - nazwa opcji) public function getXxx(); public function setXxx($value); }
Zastosowanie klasy, którą będziemy tworzyć, ma wyglądać następująco:
[PHP]
//w metodzie configure() formularza sfForm 'min' => 2, 'max' => 12, 'required' => true 'stringLengthTooShort' => 'Wprowadzona nazwa jest za krótka', 'stringLengthTooLong' => 'Wprowadzona nazwa jest za długa', )), ));
Pierwszą różnicą w API tych obydwóch interfejsów to sposób przekazywania argumentów do konstruktora. W symfony pierwszy argument to tablica opcji, zaś drugi to komunikaty błędów. W ZF opcje są przekazywane pojedyńczo jako kolejne argumenty konstruktora. Rozwiązaniem tego probelmu to:
a) Wykorzystanie refleksji do utworzenia obiektu walidatora ZF z opcjami podanymi do konstruktora adaptera, jako argumenty konstruktora
b) Wywołanie domyślnego konstruktora, opcje ustawiać pojedyńczo: w ZF każda opcja ma metody dostępowe getXxx oraz setXxx, możemy je wykorzystać do ustawienia opcji po zainicjowaniu obiektu.
Ja osobiście wybrałem wariant b), gdyż zaprzeganie refleksji do tak prostej czynności mija się z celem.
Kod konstruktora:
[PHP]
//ciach... { //obiekt Zendowskiego walidatora, można również sprawdzić typ tej klasy czy oby //na pewno jest to klasa Zend_Validate_Abstract $this->validator = new $cls(); $this->validator->setMessages($messages); //W ZF inaczej jest zrealizowane sprawdzanie czy dane pole jest wymagane, //dzieje się to na wysokości formularza, a nie walidatora, więc trzeba //w adapterze dodać tą funkcjonalność { $this->required = (bool) $options['required']; } foreach($options as $name => $value) { $this->setOption($name, $value); } } public function setOption($name, $value) { //camelize to metoda formatująca nazwę metody, zamienia np. //xxx_xxx na XxxXxx $method = 'set'.self::camelize($name); } public function setMessage($name, $value) { $this->validator->setMessage($value, $name); } //ciach...
Kolejnym krokiem jest obsługa walidacji. W symfony metoda wykonująca walidację nazywa się "clean", w ZF zaś "isValid". Argumenty mają takie same, jednak różnią się zwracaną wartością oraz sposobem sygnalizacji niepowodzenia w procesie walidacji. Klasy sfValidatorBase zwracają (przefiltrowany) ciąg wejściowy, jeśli walidacja zakończyła się sukcesem, w przeciwnym wypadku wyrzuca wyjątek sfValidatorError. Klasy Zend_Validate_Abstract zwracają true lub false odpowienio w przypadku powodzenia lub błędu. Pozatym (patrz: komentarz w poprzednim listingu) symfony i ZF różnią się miejscem sprawdzania, czy dane pole jest wymagane. Przy pisaniu adaptera trzeba mieć to na uwadze.
[PHP]
//nie wykorzystujemy metody doClean(), unieważniamy ją tak aby wyrzucała wyjątek złego użycia public function clean($value) { //dodanie obsługi sprawdzania tego, czy dane pole jest wymagane if ($this->isEmpty($value)) { if ($this->required) { throw new sfValidatorError($this, 'required'); } return $value; } $result = $this->validator->isValid($value); //podano niepoprawną wartość, utwórz i wyrzuć wyjątek if(!$result) { $errors = $this->validator->getMessages(); $errorValue = 'invalid'; { } throw new sfValidatorError($this, $errorValue); } return $value; }
Podane listingi wystarczą aby podstawowe funkcjonalności walidatorów ZF przenieść do symfony, jednakże aby adapter był kompletny trzeba nadpisać również szereg innych metod:
a) do zarządzania (ustawiania, pobierania) komunikatów błędów
b) reszta metod do zarządzania opcjami
Kod źródłowy: pobierz
Podsumowanie
Adapter stosujemy gdy klasę lub zbiór klas obcego pochodzenia chcemy wykorzystać w naszym projekcie, w którym do realizowania tej funkcjonalności co ten zewnętrzy kod, wykorzystywane jest inne api. Przykład przedstawiony w tym wpisie można uogólnić na większość frameworków. Jeśli masz swój własny framework i brakuje ci niektórych walidatorów, to zamiast pisać kilka klas, wystarczy że napiszesz jedną (adapter) i wykorzystasz już ogólnie dostępny kod. Taka idea oczywiście nie ogranicza się tylko na walidatory, to jest tylko luźny przykład aby zrozumieć praktyczne zastosowanie tego wzorca.

Komentarze (0)
Brak komentarzy.