Wzorzec Adapter zalicza się do strukturalnych wzorców projektowych i pozwala zintegrować ze sobą niekompatybilne systemy, nie naruszając przy tym istniejącej struktury, np: zewnętrzne biblioteki dołączane do programu, nie zgodność wersji platformy programistycznej (frameworka) z danym modułem, interfejsem itp.
Wzorzec ten występuje w dwóch wariantach. Implementacja może odbywać się z wykorzystaniem dziedziczenia lub kompozycji. Ja skupię się tylko na tym drugim, ze względu na większą elastyczność 🙂
SCHEMAT WZORCA ADAPTER (wariant z kompozycją)
Źródło: pl.wikipedia.org/wiki/Adapter_(wzorzec_projektowy)
PRZYKŁAD
Załóżmy, że nasz stary system webowy korzysta z bazy danych MySQL oraz sterownika mysqli, do jej obsługi. Do systemu mamy zamiar podpiąć inny podsystem, którego interfejs korzysta z PostgreSQL i sterownika PDO, który jak wiemy umożliwia komunikację z każdym typem bazy 🙂 . Oba systemy są niekompatybilne. Potrzebujemy więc „przejściówki” -adaptera, który rozwiąże ten problem i pozwoli współpracować obu bazom.
Oczywiście najlepszym wyjściem było by przepisanie klasy odpowiedzialnej za połączenie z bazą danych, ale z pewnych względów (załóżmy), nie jest to możliwe i szybszym rozwiązaniem będzie napisanie adaptera.
Na początek zbadajmy „stare” rozwiązanie, z którego korzysta nasz system. Jest to interfejs MysqliConnect i klasa implementująca go – MysqliController.
Interfejs zawiera parametry do połączenia z bazą danych i metodę linkDB(), która nawiązuje połączenie z serwerem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php // MysqliConnect.php interface MysqliConnect { const HOST = 'localhost'; const PORT = 22; const USERNAME = 'root'; const PASSWORD = '**********'; const DBNAME = 'database'; function linkDB(); } ?> |
Metoda linkDB() jest abstrakcyjna, a jej konkretna implementacja jest w poniższej klasie MysqliController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php // MysqliController.php require_once('MysqliConnect.php'); class MysqliController implements MysqliConnect { private $host = MysqliConnect::HOST; private $port = MysqliConnect::PORT; private $username = MysqliConnect::USERNAME; private $password = MysqliConnect::PASSWORD; private $dbname = MysqliConnect::DBNAME; function linkDB() { $mysqli = mysqli_connect ( $this->host, $this->username, $this->password, $this->dbname ); if (mysqli_connect_errno()) { mysqli_connect_error(); } else { echo 'Mysqli: successful connection...'; } } } ?> |
Niekompatybilny system do komunikacji z każdą bazą danych ma analogiczną strukturę. Interfejs PdoConnect posiada parametry do połączenia z serwerem bazy i definicję metody PdoLinkDb():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php // PdoConnect.php interface PdoConnect { const HOST = 'localhost'; const PORT = 22; const USERNAME = 'root'; const PASSWORD = '**********'; const DBNAME = 'database'; function PdoLinkDb(); } ?> |
Klasa PdoController implementuje interfejs PdoConnect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php // PdoController.php require_once('PdoConnect.php'); class PdoController implements PdoConnect { private $host = PdoConnect::HOST; private $port = PdoConnect::PORT; private $username = PdoConnect::USERNAME; private $password = PdoConnect::PASSWORD; private $dbname = PdoConnect::DBNAME; function PdoLinkDb() { try { $pdo = new PDO( 'pgsql:host='.$this->host.';dbname='.$this->dbname, $this->username, $this->password ); echo 'PDO: Successful connection...'; } catch(PDOException $error) { echo $error->getMessage(); } } } ?> |
Jak dotąd, kod poszczególnych klas jest bardzo prosty i nie wymaga szczegółowych analiz. Teraz, czas na naszego bohatera, czyli adaptera. Od tego momentu musisz się już bardziej skupić 🙂
Klasa adaptera DbAdapter implementuje „stary” interfejs MysqliConnect. Oprócz tego, do kodu dołączamy klasę adaptowaną PdoController. Poprzez typowanie przekazujemy do konstruktora obiekt interfejsu PdoConnect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php // DbAdapter.php include_once('MysqliConnect.php'); include_once('PdoController.php'); class DbAdapter implements MysqliConnect { private $adapter; public function __construct(PdoConnect $object) { $this->adapter = $object; } public function linkDB() { $this->adapter->PdoLinkDb(); } } ?> |
Metoda linkDB() (z interfejsu MysqliConnect) implementuje metodę PdoLinkDb() przypisując ją do instancji adaptera. Tak więc, wywołując metodę linkDB() wykonujemy kod metody adaptowanej – PdoLinkDb 🙂 .
Na koniec zapisujemy klasę klienta, która wykona żądania:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php // Client.php include_once('DbAdapter.php'); include_once('PdoController.php'); class Client { private $controller; private $adapter; public function __construct() { $this->controller = new PdoController(); $this->adapter = new DbAdapter($this->controller); $this->adapter->linkDB(); } } new Client(); ?> |
Do klasy Client dołączamy adaptera – DbAdapter i klasę adaptowaną – PdoController. Konstruktor najpierw tworzy obiekt klasy adaptowanej: new PdoController(), a następnie adapter: new DbAdapter($this->controller). Adapter pobiera instancję obiektu adaptowanego. Na koniec wywoływana jest metoda linkDB(), a w rzeczywistości kod z PdoLinkDb().
PRZYKŁAD na GitHub →