От автора: при разработке веб-приложений разработчикам очень часто приходится решать одни и те же задачи, которые с течением времени стали типовыми. Поэтому для их решения были придуманы так называемые шаблоны проектирования. В данном уроке я хотел бы рассмотреть два наиболее полезных и часто применяемых шаблона проектирования.
Введение
Как я и говорил выше, шаблоны проектирования решают типовые задачи в веб-программировании. Но это не готовое решение одной из задач, которое можно скачать и применить у себя на практике. Это — скорее всего метод, или идея того, как можно решить ту или иную задачу. Использование данных шаблонов помогает значительно ускорить процесс разработки, так как Вы не тратите время на изобретение велосипеда, помогает выработать общую стратегию в построении иерархии в создаваемом скрипте. Так же использование шаблонов проектирования помогает в документировании работы отдельных узлов Вашего веб-приложения. Так как, поясняя работу участка кода, Вы можете сказать, что он построен на базе шаблона Singtone, и знающий человек сразу поймет, в чем дело. Поэтому, знать, что такое шаблоны проектирования должен каждый хороший разработчик.
Конечно, шаблонов придумано большое количество и все их в данном уроке мы рассмотреть не сможем. Но мы рассмотрим два наиболее часто используемых, которые могут пригодиться в Вашей практике.
Singleton
Давайте рассмотрим код следующего файла:
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 |
<?php class db { public $db; public function __construct() { echo "<h1>Соединение с базой данных</h1>"; $this->db = new mysqli('localhost','root','','minicms'); if($this->db->connect_error) { throw new DbException("Ошибка соединения : "); } $this->db->query("SET NAMES 'UTF8'"); } public function get_data() { $query = "SELECT * from menu"; $result = $this->db->query($query); for($i = 0; $i < $result->num_rows; $i++) { $row[] = $result->fetch_assoc(); } return $row; } } $obj1 = new db(); $obj2 = new db(); $obj3 = new db(); ?> |
Как Вы видите, в нем описан несложный класс, предназначенный для работы с базой данных. Который, в конструкторе выполняет соединение с базой данных. То есть при создании объекта выполняется соединение с базой данных. Теперь, логично бы было, если один раз в скрипте будет создан объект данного класса. И вся последующая работа сводилась к обычному обращению к этому объекту.
Но что будет, если создать несколько объектов данного класса? Как в нашем примере. В этом случае будет три раза вызван метод конструктора, а вместе с ним будет выполнено три подключения к базе данных. Что не очень хорошо в плане быстродействия и затраченных ресурсов. Это легко проверить, если выполнить данный скрипт в браузере:
Поэтому для подобных классов, необходимо обеспечить возможность создания только одного объекта и не больше.
Для решения данной задачи применяется шаблон проектирования Singleton. Данный шаблон гарантирует, что у определенного класса можно будет создать только один объект.
Важное условие работы Singlton: необходимо закрыть публичный доступ для конструктора класса, а также запретить публичное клонирование объекта. Для этого достаточно добавить спецификатор доступа private для соответствующих методов класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private function __construct() { echo "<h1>Соединение с базой данных</h1>"; $this->db = new mysqli('localhost','root','','minicms'); if($this->db->connect_error) { throw new DbException("Ошибка соединения : "); } $this->db->query("SET NAMES 'UTF8'"); } private function __clone() { } |
Теперь опишем метод реализующий шаблон Singleton (я приведу весь исправленный код класса):
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 37 38 39 40 41 42 43 44 |
<?php class db { public $db; public $i = 1; static private $_ins = NULL; static public function get_instance() { if(self::$_ins instanceof self) { return self::$_ins; } return self::$_ins = new self; } public function __construct() { echo "<h1>Соединение с базой данных</h1>"; $this->db = new mysqli('localhost','root','','minicms'); if($this->db->connect_error) { throw new DbException("Ошибка соединения : "); } $this->db->query("SET NAMES 'UTF8'"); } private function __clone() { } public function get_data() { $query = "SELECT * from menu"; $result = $this->db->query($query); for($i = 0; $i < $result->num_rows; $i++) { $row[] = $result->fetch_assoc(); } return $row; } public function i() { return $this->i++; } } |
Для работы Singleton необходимы: статическое свойство $_ins (по умолчанию содержит в себе NULL) и статический метод get_instance(). Свойство $_ins – будет хранить в себе объект класса db. Статический метод get_instance() – гарантирует создание только одного экземпляра объекта класса. Смотрите, так как мы условились, что в свойстве $_ins будет содержаться объект класса. Значит первым делом в методе get_instance() мы выполним проверку – если в данном свойстве содержится объект класса db, значит, мы возвращаем значение данного свойства. В другом случае мы создадим объект данного класса, запишем его в свойство $_ins и вернем его как результат отработки данного метода.
Это и есть реализация шаблона Singleton. Теперь давайте посмотрим, как создать объект, используя данный шаблон. Так как напрямую создать объект класса уже нельзя, потому как конструктор класса закрыт. Создать объект класса можно простым обращение к методу get_instance():
1 2 3 |
$obj1 = db::get_instance(); $obj2 = db::get_instance(); $obj3 = db::get_instance(); |
Давайте посмотрим, что у нас получилось:
Как Вы видите, теперь конструктор класса вызывается только один раз, потому что мы работаем только с одним объектом класса. Для дополнительной проверки, можно вызвать метод i(), который я добавил для примера. Если мы вот таким образом вызовем несколько раз данный метод:
1 2 3 4 5 6 |
$obj1 = db::get_instance(); echo ($obj1->i()); $obj2 = db::get_instance(); echo ($obj2->i()); $obj3 = db::get_instance(); echo ($obj3->i()); |
То на экране мы увидим следующее:
Это доказывает, что мы работаем с одним и тем же объектом класса db.
Fabric
Шаблон проектирования Fabric – отвечает за создание объектов определенных классов. Данный шаблон рационально использовать, когда необходимо реализовать автоматическую загрузку файлов с классами и последующим созданием объектов этих классов. А также когда у определенного набора классов при создании их объектов, необходимо вызывать дополнительные методы, практически одинаковые для каждого из них. И что бы для каждого класса не прописывать вызов одних и тех же методов. Удобно вынести их вызов в отдельный класс и обращаться к нему как к посреднику при создании объектов. Итак, простейший пример шаблона Fabric.
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 |
<?php class test { public function __construct() { echo "Это класс - ".__CLASS__; } public function init() { $this->i = "True"; } } class get { static public function create($item) { ///// /// return new test(); } } $o = get::create('str'); var_dump($o); ?> |
К примеру, есть некий класс test. Для создания его объекта, мы создаем дополнительный класс get, со статическим методом create(), который создаст и вернет объект класса test(). При этом после создания объекта в методе create(), можно вызвать различные методы класса test, если конечно они нужны по логике работы скрипта.
Следующий пример – это уже более реальный случай. В данном примере мы создаем механизм автоматической загрузки файлов с нужными классами.
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 37 38 39 40 |
<?php class test { public function __construct() { echo "Это класс - ".__CLASS__; } public function init() { $this->i = "True"; } } class example { public function __construct() { echo "<br/>Это класс - ".__CLASS__; } public function init(){ $this->i = FALSE; } } class get { public static function create($class) { if(file_exists('classes/'.$class.".php")) { include_once 'classes/'.$class.".php"; if(class_exists($class)) { $o = new $class; $o->init(); return $o; } } } } get::create('test'); get::create('example'); ?> |
К примеру, по логике нашего скрипта, нам необходимо подключать классы test и example, которые расположены в отдельных файлах. Для этого мы создаем класс get, со статическим методом create(). Параметром данный метод принимает имя класса, объект которого необходимо создать. Классы test и example сохранены в файлах имена, которых такие же, как и имена классов. Поэтому, когда передается имя класса — в метод create(), также передается и имя файла, в котором расположен данный класс. Далее остается проверить существование данного файла и нужного класса в нем, подключить данный файл и создать его объект. Опять же можно вызывать различные дополнительные методы. В нашем примере дополнительный метод – это метод init(). Давайте посмотри в браузере, что у нас получилось:
Третий пример шаблона Fabric. К примеру, есть следующая иерархия классов:
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 |
<?php abstract class Car { public $color; public function __construct($color) { $this->color = $color; echo $this->color."<br/>"; } } class Toyota extends Car { public function __construct($color) { echo parent::__construct($color); } } class Mazda extends Car { public function __construct($color) { echo parent::__construct($color); } } class Ford extends Car { public function __construct($color) { echo parent::__construct($color); } } ?> |
Опять же классы очень простые и созданы только для примера. Смотрите есть родительский абстрактный класс Car, в котором описан метод конструктора. И есть три его потомка классы Toyota, Mazda и Ford. Для создания объектов этих классов, мы создадим дополнительный класс и метод посредник, который в зависимости от передаваемых аргументов будет создавать объект одного из классов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class get { static public function create($type,$color) { switch($type) { case 'Toyota': return new Toyota($color); break; case 'Mazda': return new Mazda($color); break; case 'Ford': return new Ford($color); break; } } } $car = get::create('Ford','green'); var_dump($car); |
Как Вы видите в данном классе в методе get() использован обычный условный оператор switch(), который в зависимости от значения параметра $type создает объект нужного класса. На экране мы увидим следующее:
Шаблон проектирования Fabric как раз и показывает, что шаблоны это не готовое решение – это метод как можно организовать логику и построение иерархии Вашего скрипта.
Это и все что я хотел показать Вам в данном уроке. Всего Вам доброго и удачного кодирования!!!
Комментарии (4)