Wzorce projektowe – Singleton
Singleton jest przykładem kreacyjnego wzorca projektowego, czyli takiego, którego zadaniem jest tworzenie obiektów używanych w aplikacji. W artykule została przedstawiona idea stojąca za wzorcem singleton, przykład użycia i wynikające z niego korzyści.
Charakterystyka wzorca singleton
Singleton należy do grupy kreacyjnych wzorców projektowych, którego zadaniem jest ograniczenie możliwości tworzenia obiektów danej klasy do tylko jednej instancji oraz zapewnienie globalnego dostępu do tego obiektu.
Diagram UML przedstawia konstrukcję klasy implementującej wzorzec:
Jak widzimy implementacja wzorca jest bardzo prosta i mieści się w jednej klasie. Mamy tutaj kilka elementów:
- __construct() – prywatny konstruktor, który zapobiega utworzenie instancje klasy na zewnątrz tej klasy
- getInstance() – statyczna metoda umożliwiająca klientowi dostęp do niepowtarzalnego egzemplarza klasy z dowolnego miejsca w kodzie
- instance – statyczna właściwość przechowująca instancję utworzonej klasy
Przykład zastosowania wzorca singleton – klasa obsługująca połączeniem z bazą danych
Klasycznym przykładem zastosowania wzorca singleton jest klasa umożliwiająca połączenie z bazą danych systemu. Zazwyczaj, podczas jednej sesji użytkownika chcemy, aby takie połączenie zostało utworzone tylko jeden raz. Oczywiście zdarzają się sytuacje, gdy z jakiegoś powodu chcemy nawiązać połączenie ponownie, ale załóżmy że to nie jest teki przypadek.
Do dzieła, stwórzmy klasę, której zadaniem będzie połączenie się z bazą danych.
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 |
<?php namespace DesignPatterns\Singleton\DatabaseManager; class DatabaseConnector { private static $instance; private $connection; private function __construct() {} public static function getInstance(): DatabaseConnector { if (!self::$instance) { self::$instance = new DatabaseConnector(); self::$instance->makeConnection(); } return self::$instance; } private function makeConnection() { echo 'Connecting to database...'; $this->connection = 'CONNECTION'; } public function executeQuery(string $sql) { echo 'Executing query: ' . $sql . ' for connection: ' . $this->connection; } } |
Dla uproszczenia, tak samo jak w każdym z poprzednich artykułów z serii „wzorce projektowe”, pomijam kwestie faktycznej implementacji danej funkcjonalności. Na potrzeby zademonstrowania idei wzorca, nie ma konieczności skupianie się na detalach.
Jak widzimy, zastosowanie wzorca jest banalnie proste. Mamy naszą klasę, która implementuje statyczną metodę getInstance. Jej zadaniem jest sprawdzenie czy instancja nie została już utworzona. Jeżeli nie, to taki obiekt zostaje stworzony a następnie zostaje wywołana metoda, nawiązująca połączenie z bazą (makeConnection).
Mamy również prywatny konstruktor, który zapobiega przed utworzeniem instancji tej klasy w innym miejscu w kodzie. Innymi słowy, instancja klasy DatabaseConnector może być utworzona jedynie przez samą siebie, za pośrednictwem zaimplementowanej metody statycznej.
Użycie takiej klasy jest banalnie proste.
1 2 3 4 5 6 7 8 9 |
<?php require_once '../../../vendor/autoload.php'; use DesignPatterns\Singleton\DatabaseManager\DatabaseConnector; $databaseConnector = DatabaseConnector::getInstance(); $databaseConnector->executeQuery('SELECT * FROM users;'); |
Powyższy przykład można zobaczyć i pobrać na GitHub: https://github.com/molitorys/design-patterns/tree/master/src/Singleton/DatabaseManager
Podsumowanie
Zastosowanie wzorca singleton daje nam kilka korzyści:
- zapewnia kontrolę tworzenia i dostępu do egzemplarza instancji klasy oraz w razie potrzeby umożliwia określenie limitu liczby egzemplarzy
- daje możliwość konfiguracji tworzonego obiektu tylko raz podczas jego tworzenia
- jest zamiennikiem dla zmiennych globalnych