- PVSM.RU - https://www.pvsm.ru -
Вы когда-либо задавались вопросом, что такое шаблоны проектирования? В этой статье будет разъяснено, почему шаблоны проектирования имеют существенное значение, и будет приведено несколько примеров на PHP, поясняющих, когда и где их следует использовать.
Шаблоны проектирования — это допускающие многократное использование оптимизированные решения проблем программирования, с которыми мы сталкиваемся каждый день. Шаблон проектирования — это не класс или библиотека, которые мы можем просто вставить в нашу систему. Он — много больше. Это — некоторый шаблон, который должен быть реализован в надлежащей ситуации. Он не зависит от языка. Хороший шаблон проектирования должен быть таким, чтобы его можно было использовать с большинством языков (если не со всеми) в зависимости от характеристик языка. Чрезвычайно важно то, что любой шаблон проектирования необходимо использовать очень осторожно — если он применён в ненадлежащем месте, то его действие может быть разрушительным и породить много проблем для вас. Однако применённый в нужном месте в нужное время он может стать вашим спасителем.
Есть три основных типа шаблонов проектирования:
• структурный
• порождающий
• поведенческий
Структурные шаблоны, в общем случае, имеют дело с отношениями между объектами, облегчая их совместную работу.
Порождающие шаблоны обеспечивают механизмы инстанцирования, облегчая создание объектов способом, который наиболее соответствует ситуации.
Поведенческие шаблоны используются в коммуникации между объектами, делая её более лёгкой и гибкой.
Шаблоны проектирования в принципе являются хорошо продуманными решениями проблем программирования. Многие программисты уже сталкивались ранее с этими проблемами и использовали для преодоления эти «решения». Встречая какую-то проблему, зачем заново искать решение, когда можно применить уже проверенное?
Давайте представим, что вам поручено создать способ объединить два класса, которые выполняют два разных действия в зависимости от ситуации. Эти два класса интенсивно используются существующей системой в разных местах, что затрудняет их удаление и изменение существующего кода. Дополнительно к этому изменение существующего кода требует проверки всего изменённого кода, т.к. правка такого рода в системе, построенной на разных компонентах, почти всегда вносит новые ошибки. Вместо перечисленного можно использовать вариант шаблона «Стратегия» и шаблона «Адаптер», которые могут легко обращаться с названными типами сценариев.
01 <?php
02 class StrategyAndAdapterExampleClass {
03 private $_class_one;
04 private $_class_two;
05 private $_context;
06
07 public function __construct( $context ) {
08 $this->_context = $context;
09 }
10
11 public function operation1() {
12 if( $this->_context == "context_for_class_one" ) {
13 $this->_class_one->operation1_in_class_one_context();
14 } else ( $this->_context == "context_for_class_two" ) {
15 $this->_class_two->operation1_in_class_two_context();
16 }
17 }
18 }
Довольно просто, не так ли? Теперь посмотрим внимательнее на шаблон «Стратегия».
Изображение размещено с разрешения владельцев сайта cioinnervoice.wordpress.com [1]
Шаблон «Стратегия» является поведенческим шаблоном проектирования, который позволяет вам решать, какой план действий должна принять программа, основываясь на определённом контексте при выполнении. Вы закладываете два различных алгоритма внутри двух классов и решаете в ходе выполнения, с какой стратегией следует работать.
В нашем примере выше стратегия устанавливается в зависимости от того, какой была переменная $context в то время, когда класс подвергался обработке. Если вы даёте ей контекст для класса_один, то будет использован класс_один и наоборот.
Предположим, что вы в данный момент разрабатываете класс, который может или обновить или создать новую пользовательскую запись. Хотя ему требуются те же самые входы (имя, адрес, номер мобильного телефона и т.п.), но в зависимости от ситуации он должен использовать различные функции при обновлении и создании. Здесь для выполнения можно, вероятно, сразу же использовать условный переход «if-else», но как быть, если этот класс понадобится и в другом месте? Тогда нужно будет переписать полностью весь этот оператор условного перехода. Не было бы проще просто указать ваш контекст?
01 <?php
02 class User {
03
04 public function CreateOrUpdate($name, $address, $mobile, $userid = null)
05 {
06 if( is_null($userid) ) {
07 // Это значит, что пользователь ещё не существует, надо создать новую запись
08 } else {
09 // Это значит, что пользователь уже существует, необходимо обновить на базе данного идентификатора пользователя
10 }
11 }
12 }
Теперь «обычный» шаблон «Стратегия» предполагает помещение ваших алгоритмов внутри другого класса, но в данном случае другой класс был бы нерациональным решением. Помните, что вы не обязаны точно следовать шаблону. Варианты работают, пока концепция остаётся прежней, и это решает проблему.
Изображение размещено с разрешения владельцев сайта www.uxcell.com [2]
Шаблон «Адаптер» является структурным шаблоном проектирования, который позволяет перепрофилировать класс с другим интерфейсом, делая его доступным для системы, которая использует различные методы вызова.
Это также позволяет изменять некоторые из входов, получаемых от класса клиента, превращая его в нечто совместимое с функциями класса Adaptee.
Другим понятием для ссылки на класс адаптера является «обёртка», которая по существу позволяет «обернуть» действия в класс и повторно использовать эти действия в надлежащих ситуациях. Классическим примером может быть создание доменного класса для классов таблиц. Вместо вызова различных классов таблиц и последовательного вызова их функций можно вложить все эти методы в один, используя класс адаптера. Это не только позволяет вам повторно использовать любые требуемые действия, но также избавляет от необходимости переписывать код, если вам нужно использовать то же действие в другом месте.
Сравните эти две реализации:
Подход без адаптера
1 <?php
2 $user = new User();
3 $user->CreateOrUpdate( //inputs );
4
5 $profile = new Profile();
6 $profile->CreateOrUpdate( //inputs );
Если бы нам нужно было сделать это снова в другом месте или даже использовать этот код в другом проекте, то мы должны были бы набрать всё заново.
Лучше
Указанное противоположно действиям вроде приведённых ниже:
1 <?php
2 $account_domain = new Account();
3 $account_domain->NewAccount( //inputs );
В данной ситуации мы имеем обёрточный класс, который был бы нашим доменным классом Account:
01 <?php
02 class Account()
03 {
04 public function NewAccount( //inputs )
05 {
06 $user = new User();
07 $user->CreateOrUpdate( //subset of inputs );
08
09 $profile = new Profile();
10 $profile->CreateOrUpdate( //subset of inputs );
11 }
12 }
Таким образом, можно использовать домен Account снова везде, где требуется, — дополнительно вы оказываетесь в состоянии обёртывать другие классы также под вашим доменным классом.
Изображение размещено с разрешения владельцев сайта www.lankanewspappers.com [3]
Шаблон «Фабричный метод» является порождающим шаблоном проектирования, который делает именно то, что означает это слово: этот класс действует как фабрика экземпляров объектов.
Основной целью этого шаблона является вложение порождающей процедуры, которая может свести различные классы в одну функцию. Если фабричному методу обеспечить надлежащий контекст, то он будет в состоянии вернуть правильный объект.
Наилучшей ситуацией для использования шаблона «Фабричный метод» является наличие нескольких различных вариантов одного объекта. Допустим, имеется класс «кнопка»; у этого класса есть различные варианты — например, ImageButton (кнопка изображения), InputButton (кнопка ввода) и FlashButton (флэш-кнопка). В зависимости от места может потребоваться создать различные кнопки — именно здесь можно использовать «фабрику» для создания кнопок для вас!
Начнём с создания наших трёх классов:
01 <?php
02 abstract class Button {
03 protected $_html;
04
05 public function getHtml()
06 {
07 return $this->_html;
08 }
09 }
10
11 class ImageButton extends Button {
12 protected $_html = "..."; //Здесь должен быть задан HTML, требуемый для вашей кнопки на базе изображения
13 }
14
15 class InputButton extends Button {
16 protected $_html = "..."; //Здесь должен быть задан HTML, требуемый для вашей нормальной кнопки (<input type="button"... />);
17 }
18
19 class FlashButton extends Button {
20 protected $_html = "..."; //Здесь должен быть задан HTML, требуемый для вашей флэш-кнопки
21 }
Теперь можно создать наш фабричный класс:
01 <?php
02 class ButtonFactory
03 {
04 public static function createButton($type)
05 {
06 $baseClass = 'Button';
07 $targetClass = ucfirst($type).$baseClass;
08
09 if (class_exists($targetClass) && is_subclass_of($targetClass, $baseClass)) {
10 return new $targetClass;
11 } else {
12 throw new Exception("The button type '$type' is not recognized.");
13 }
14 }
15 }
Полученный код можно использовать, например, так:
1 $buttons = array('image','input','flash');
2 foreach($buttons as $b) {
3 echo ButtonFactory::createButton($b)->getHtml()
4 }
Выходом должен быть HTML всех ваших типов кнопок. Таким образом, вы могли бы указать, какую кнопку создать в зависимости от ситуации, а также повторно использовать условие.
Изображение размещено с разрешения владельцев сайта www.decoratorsdarlington.co.uk [4]
Шаблон «Декоратор» является структурным шаблоном проектирования, который позволяет нам добавлять новое или дополнительное поведение к объекту в ходе выполнения в зависимости от ситуации.
Целью является сделать так, чтобы расширенные функции могли быть применены к одному конкретному экземпляру и в то же время иметь возможность создать оригинальный экземпляр, который не имеет этих новых функций. Это также позволяет комбинировать несколько декораторов для одного экземпляра, благодаря чему отсутствует привязка к одному декоратору для каждого экземпляра. Этот шаблон является альтернативой созданию подкласса, что относится к созданию класса, который наследует функциональность от родительского класса. В противоположность к созданию подкласса, которое добавляет поведение во время компиляции, «декорирование» позволяет добавить новое поведение в ходе выполнения, если ситуация требует этого.
Чтобы реализовать шаблон «Декоратор», можно выполнить следующие шаги:
1. Выделите оригинальный класс «Компонент» как подкласс класса «Декоратор».
2. В классе «Декоратор» добавьте указатель «Компонент» как поле.
3. Переместите «Компонент» в конструктор «Декоратора», чтобы инициализировать указатель «Компонент».
4. В классе «Декоратор» перенаправьте все методы «Компонент» на указатель «Компонент».
5. В классе «Декоратор» отмените любой метод (любые методы) «Компонент», поведение которого (которых) должно быть модифицировано.
Данные этапы размещены с разрешения владельцев сайта en.wikipedia.org/wiki/Decorator_pattern [5]
Наиболее удобно использовать шаблон «Декоратор» для объекта, требующего нового поведения, только когда на это имеется запрос по ситуации. Допустим, имеется HTML-элемент компоновки, ссылка на выход из системы, и вы желаете, чтобы она делала немного различающиеся действия, основываясь на текущей странице. Для этого можно использовать шаблон «Декоратор».
Сначала зададим различные требуемые «декорации»:
• Если мы находимся на главной странице и зарегистрированы, то эта ссылка должна быть «обёрнута» в теги h2.
• Если мы находимся на другой странице и зарегистрированы, то эта ссылка должна быть «обёрнута» в теги подчёркивания.
• Если мы зарегистрированы, то эта ссылка должна быть «обёрнута» в теги полужирного шрифта.
После задания наших «декораций» можно их запрограммировать:
01 <?php
02 class HtmlLinks {
03 //Некоторые способы, имеющиеся для всех html-ссылок
04 }
05
06 class LogoutLink extends HtmlLinks {
07 protected $_html;
08
09 public function __construct() {
10 $this->_html = "<a href="logout.php">Logout</a>";
11 }
12
13 public function setHtml($html)
14 {
15 $this->_html = $html;
16 }
17
18 public function render()
19 {
20 echo $this->_html;
21 }
22 }
23
24 class LogoutLinkH2Decorator extends HtmlLinks {
25 protected $_logout_link;
26
27 public function __construct( $logout_link )
28 {
29 $this->_logout_link = $logout_link;
30 $this->setHtml("<h2>" . $this->_html . "</h2>");
31 }
32
33 public function __call( $name, $args )
34 {
35 $this->_logout_link->$name($args[0]);
36 }
37 }
38
39 class LogoutLinkUnderlineDecorator extends HtmlLinks {
40 protected $_logout_link;
41
42 public function __construct( $logout_link )
43 {
44 $this->_logout_link = $logout_link;
45 $this->setHtml("<u>" . $this->_html . "</u>");
46 }
47
48 public function __call( $name, $args )
49 {
50 $this->_logout_link->$name($args[0]);
51 }
52 }
53
54 class LogoutLinkStrongDecorator extends HtmlLinks {
55 protected $_logout_link;
56
57 public function __construct( $logout_link )
58 {
59 $this->_logout_link = $logout_link;
60 $this->setHtml("<strong>" . $this->_html . "</strong>");
61 }
62
63 public function __call( $name, $args )
64 {
65 $this->_logout_link->$name($args[0]);
66 }
67 }
Затем мы должны быть в состоянии использовать это, например, следующим образом:
01 $logout_link = new LogoutLink();
02
03 if( $is_logged_in ) {
04 $logout_link = new LogoutLinkStrongDecorator($logout_link);
05 }
06
07 if( $in_home_page ) {
08 $logout_link = new LogoutLinkH2Decorator($logout_link);
09 } else {
10 $logout_link = new LogoutLinkUnderlineDecorator($logout_link);
11 }
12 $logout_link->render();
Здесь можно видеть, как мы оказываемся в состоянии скомбинировать несколько декораторов, если это требуется. Поскольку все декораторы используют магическую функцию __call, то мы можем всё ещё вызвать методы оригинальной функции. Если принять, что мы в настоящий момент находимся внутри главной страницы и зарегистрированы, то HTML-выход должен быть следующим:
1 <strong><h2><a href="logout.php">Logout</a></h2></strong>
Изображение размещено с разрешения владельцев сайта intoxicologist.wordpress.com [6]
Шаблон проектирования «Одиночка» является порождающим шаблоном проектирования, который обеспечивает наличие одного единственного экземпляра какого-то конкретного класса во время выполнения и глобальную точку доступа к этому единственному экземпляру.
Это облегчает задание точки «координации» для других объектов, также использующих данный единственный экземпляр, поскольку его переменные будут неизменными для любых вызовов.
Если требуется перевести какой-либо специфический экземпляр из одного класса в другой, то можно использовать шаблон «Одиночка», чтобы устранить проведение этого экземпляра через конструктор или аргумент. Предположим, вы создали класс Session, который имитирует глобальный массив $_SESSION. Поскольку для этого класса его экземпляр требуется создать только один раз, то можно реализовать шаблон «Одиночка», как, например:
01 <?php
02 class Session
03 {
04 private static $instance;
05
06 public static function getInstance()
07 {
08 if( is_null(self::$instance) ) {
09 self::$instance = new self();
10 }
11 return self::$instance;
12 }
13
14 private function __construct() { }
15
16 private function __clone() { }
17
18 // Мы можем использовать любые другие способы сеанса
19 ...
20 ...
21 ...
22 }
23
24 // Получить экземпляр сеанса
25 $session = Session::getInstance();
Сделав так, мы можем получать доступ к нашему экземпляру сеанса из различных частей нашего кода, даже в разных классах. Эти данные будут сохраняться в течение всех вызовов getInstance.
Имеется ещё много шаблонов проектирования, которые полезно знать; в этой статье я остановился только на некоторых из наиболее известных, которые я использую при программировании. Если есть интерес прочитать о других шаблонах проектирования, то страница Википедии Design Patterns [7] (Шаблоны проектирования) содержит много информации об этом. Если этого недостаточно, то вы всегда можете прочитать книгу «Design Patterns: Elements of Reusable Object-Oriented Software» [8] («Шаблоны проектирования: элементы многократно используемого объектно-ориентированного программного обеспечения»), которая считается одной из лучших по рассматриваемой теме.
И последнее: используя эти шаблоны проектирования, всегда проверяйте, что вы пытаетесь решить правильно поставленную задачу. Как я упоминал ранее, эти шаблоны проектирования необходимо использовать очень осторожно: они — при использовании в ненадлежащем контексте — могут ухудшить ситуацию, но при правильном использовании они просто жизненно необходимы.
Если вы нашли данную статью полезной, то почему бы не ознакомиться с предложением PHP-скриптов [9] на Envato Market. Там представлены тысячи полезных скриптов, которые могут ускорить вашу разработку и улучшить конечный результат. Имеются системы бронирования [10], контактные формы AJAX [11], системы информационных бюллетеней [12] и многое другое.
Автор: LukinB
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/176024
Ссылки в тексте:
[1] cioinnervoice.wordpress.com: http://cioinnervoice.wordpress.com
[2] www.uxcell.com: http://www.uxcell.com
[3] www.lankanewspappers.com: http://www.lankanewspappers.com
[4] www.decoratorsdarlington.co.uk: http://www.decoratorsdarlington.co.uk
[5] en.wikipedia.org/wiki/Decorator_pattern: http://en.wikipedia.org/wiki/Decorator_pattern
[6] intoxicologist.wordpress.com: http://intoxicologist.wordpress.com
[7] Design Patterns: http://en.wikipedia.org/wiki/Design_pattern_%28computer_science%29
[8] «Design Patterns: Elements of Reusable Object-Oriented Software»: http://www.amazon.com/o/asin/0201633612
[9] PHP-скриптов: http://codecanyon.net/category/php-scripts
[10] системы бронирования: http://codecanyon.net/item/booking-system/87919
[11] контактные формы AJAX: http://codecanyon.net/item/ajax-contact-form/50846
[12] системы информационных бюллетеней: http://codecanyon.net/item/newsletter-system/52667
[13] Источник: https://habrahabr.ru/post/307942/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.