Сегодня мы начинаем довольно сложную тему шаблонов проектирования (Design patterns).
Шаблон проектирования по сути представляет собой подход к решению типовой задачи, который был неоднократно применен, признан среди специалистов, получил имя и довольно широкую известность.
Так в математике для нахождения корней квадратного уравнения в школе часто используют дискриминант. Никто не говорит: а давайте с вами попробуем использовать формулу, которая берет второй коэффициент со знаком минус, прибавляет или вычитает из этого коэффициента квадратный корень из суммы квадрата второго коэффициента и так далее... нет, все просто называют способ нахождения корней через дискриминант и имеют в виду одно и то же решение.
По такому же принципу работают и шаблоны проектироания.
В 1991 году Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес (John Vlissides) публикуют книгу Design Patterns — Elements of Reusable Object-Oriented Software.
В этой книге описаны 23 шаблона проектирования. Также команда авторов этой книги известна общественности под названием «Банда четырёх» (англ. Gang of Four, часто сокращается до GoF). До появления этой книги и предшествовавших ей работ ее авторов никто не формализовал и не использовал шаблоны проектирования. После выхода книги тема стала очень популярной.
Основные виды шаблонов проектирования:
- порождающие
- структурные
- поведенческие
- архитектурные
Порождающие шаблоны проектирования управляют процессом создания новых объектов. Применяются в случае, если классическое создание объекта не эффективно или противоречит общей задаче.
Есть 6 классических порождающих шаблонов проектирования:
- Простая фабрика
- Фабричный метод
- Абстрактная фабрика
- Строитель
- Прототип
- Одиночка
Структурные шаблоны проектирования предлагают варианты взаимного использования объектов, стуктурной их вложенности друг в друга, композиции объектов в более сложные объекты.
Есть 7 классических структурных шаблонов проектирования:
- Адаптер
- Мост
- Компоновщик
- Декоратор
- Фасад
- Приспособленец
- Заместитель
Поведенческие шаблоны проектирования, в отличие от структурных, не только определяют варианты взаимного использования объектов и сущностей, но и пытаются описывать способы их взаимодействия, их реализацию.
Десятка классических поведенческих шаблонов проектирования:
- Цепочка ответственности
- Команда
- Итератор
- Посредник
- Хранитель
- Наблюдатель
- Посетитель
- Стратегия
- Состояние
- Шаблонный метод
Выше перечислены лишь 23 классических шаблона проектирования от "Банды четырех". Помимо этих шаблонов есть масса других, в том числе как этих трех видов, так и архитектурные, а могут быть и еще каких-то дополнительных видов.
В этом уроке мы рассмотрим два довольно простых, но важных и интересных порождающих шаблона: Одиночка (Singleton) и и Простая фабрика (Simple Fabric)
###Шаблон проектирования Singleton
Одним из самых известных и популярных шаблонов проектирования в целом является шаблон Singleton.
Задача: существует класс и есть необходимость создать объект этого класса, но есть нюанс: необходимо проконтролировать, чтобы такой объект создавался только один. Второго такого объекта желательно не допускать. Эту задачу решает шаблон проектированя Singleton (Одиночка).
Классический пример такой задачи - подключение к базе данных. Каждое подключение к базе занимает место в памяти и требует времени для создания и удаления. Для одного, двух, пяти, десяти, сотни запросов к базе достаточно иметь всего лишь одно подключение. Чтобы избежать большого количества объектов подключения к базе данных, часто рекомендуется использовать Singleton.
Для начала, создадим класс, который будет подключаться к БД:
<?php
class DB {
function __construct(){
$this->con = new mysqli("127.0.01", "root", "12345", "books");
}
}
$db = new DB();
print_r($db->con->query("SELECT title FROM books")->fetch_all());
Итак, у нас есть класс, который в конструкторе подключается к БД, подключение работает и позволяет выполнять запросы к БД.
Ничего на данный момент не мешает нам создать второй объект и выполнить второе подключение к той же БД. Необходимо каким-то образом запретить создавать объекты этого класса. Применим наши знания принципов ООП и магических методов, а имено: ограничим доступ к конструктору объекта, добавив ему спецификатор доступа private
.
После такого изменения мы не сможем создавать новые объекты. Естественно, мы так же потеряем возможность создавать тот самый единственный способ.
Единственный способ создавать объект (при помощи ключего слова new
) - создавать объект внутри класса, т.е. в рамках какого-то метода. Но. Обычные методы класса мы запускаем только посредством объекта класса, который мы как раз и не можем создать.
Тут нам на помощь приходят статические методы и свойства. Как нам известно, статические методы принадлежат классу, а значит, могут работать с приватными свойствами и методами класса. С другой стороны, они не требуют объекта и работают напрямую с классом. Применим эту идею на практике:
<?php
class DB {
private function __construct(){
$this->con = new mysqli("127.0.01", "root", "12345", "books");
}
static public function getConnect(){
return (new self())->con;
}
}
$con = DB::getConnect();
print_r($con->query("SELECT title FROM books")->fetch_all());
...отлично. Применили на практике знания о статике, но мы все равно имеем возможность создавать несколько объектов, просто более сложным путем. Но тут уже все проще, нам достаточно каким-то образом проверять, создавали ли мы объект, и не позволять создавать новые.
Поскольку создание происходит в статическом методе, хранить информацию о том, что мы уже создавали объект, нам предстоит тоже в статическом свойстве, другие нам некуда писать.
class DB {
static $obj;
private function __construct(){
$this->con = new mysqli("127.0.01", "root", "12345", "books");
}
static public function getConnect(){
if(empty(self::$obj))
self::$obj = new self();
return self::$obj->con;
}
}
$con = DB::getConnect();
print_r($con->query("SELECT title FROM books")->fetch_all());
Варианты хранить bool переменную или счетчик созданных объектов нам в данном случае не подходит, ведь наш метод должен не только не позволять создавать новые объекты, но и как-то позволять работать со старым. Будем сохранять этот объект. Мы практически завершили написание шаблона проектирования Singleton, остался только один момент - закрыть еще два возможных варианта создания объектов, а именно: десериализацию и клонирование:
<?php
class DB {
static $obj;
private function __clone(){}
private function __wakeup(){}
private function __construct(){
$this->con = new mysqli("127.0.01", "root", "12345", "books");
}
static public function getConnect(){
if(empty(self::$obj))
self::$obj = new self();
return self::$obj->con;
}
}
$con = DB::getConnect();
print_r($con->query("SELECT title FROM books")->fetch_all());
Мы реализовали самый известный, простой и популярный шаблон проектирования Singleton. На всякий случай имеет смысл прочесть статью на тему минусов его применения на сайте troger.ru.
- Написать класс для работы с книгами в базе данных.
- Написать с нуля для него подключение к БД с использованием шаблона проектирования Singleton.
- Почитать про другие шаблоны проектирования как в "банде четырех", так и в других книгах (рекомендую книги авторов Мэтта Зандстры, Александра Швеца), почитать статьи по ссылкам: