Що таке шаблони проектування?

Ви коли-небудь задавалися питанням, що таке шаблони проектування? У цій статті буде роз'яснено, чому шаблони проектування мають істотне значення, та буде наведено кілька прикладів на 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

Шаблон «Стратегія» є поведінковим шаблон проектування, який дозволяє вам вирішувати, який план дій повинна прийняти програма, ґрунтуючись на певному контексті при виконанні. Ви закладаєте два різних алгоритму всередині двох класів і вирішуєте в ході виконання, з якою стратегією слід працювати.

У нашому прикладі вище стратегія встановлюється залежно від того, якою була мінлива $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

Шаблон «Адаптер» є структурним шаблон проектування, який дозволяє перепрофілювати клас з іншим інтерфейсом, роблячи його доступним для системи, яка використовує різні методи виклику.

Це також дозволяє змінювати деякі з входів, одержуваних від класу клієнта, перетворюючи його в щось сумісний з функціями класу 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

Шаблон «Фабричний метод» є породжуючою шаблон проектування, який робить саме те, що означає це слово: цей клас діє як фабрика примірників об'єктів.

Основною метою цього шаблону є вкладення породжує процедури, яка може звести різні класи в одну функцію. Якщо заводському методу забезпечити належний контекст, то він буде в змозі повернути правильний об'єкт.

Коли можна використовувати це?


Найкращою ситуацією для використання шаблону «Фабричний метод» є наявність декількох різних варіантів одного об'єкта. Припустимо, є клас «кнопка»; у цього класу є різні варіанти — наприклад, 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

Шаблон «Декоратор» є структурним шаблон проектування, який дозволяє нам додавати нове або додаткове поведінку об'єкта в ході виконання в залежності від ситуації.

Метою є зробити так, щоб розширені функції могли бути застосовані до одного конкретного екземпляра і в той же час мати можливість створити оригінальний примірник, який не має цих нових функцій. Це також дозволяє комбінувати кілька декораторів для одного примірника, завдяки чому відсутня прив'язка до одного декоратора для кожного екземпляра. Цей шаблон є альтернативою створенню підкласу, що відноситься до створення класу, який успадковує функціональність батьківського класу. В протилежність до створення підкласу, яке додає поведінку під час компіляції, «декорування» дозволяє додати нову поведінку в ході виконання, якщо ситуація вимагає цього.

Щоб реалізувати шаблон «Декоратор», можна виконати наступні кроки:

1. Виділіть оригінальний клас «Компонент» як підклас класу «Декоратор».
2. У класі «Декоратор» додайте покажчик «Компонент» як поле.
3. Перемістіть «Компонент» конструктор «Декоратора», щоб ініціалізувати покажчик «Компонент».
4. У класі «Декоратор» перенаправте всі методи «Компонент» на покажчик «Компонент».
5. У класі «Декоратор» відмініть будь-який метод (будь-які методи) «Компонент», поведінка якого (яких) повинна бути модифіковано.

Дані етапи розміщені з дозволу власників сайту en.wikipedia.org/wiki/Decorator_pattern

Коли можна використовувати це?


Найбільш зручно використовувати шаблон «Декоратор» для об'єкта, що вимагає нового поведінки, тільки коли на це є запит щодо ситуації. Припустимо, є 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

Шаблон проектування «Одинак» є породжуючою шаблон проектування, який забезпечує наявність одного єдиного примірника якогось конкретного класу під час виконання та глобальну точку доступу до цього єдиному примірнику.

Це полегшує завдання точки «координації» для інших об'єктів, також використовують цей єдиний екземпляр, оскільки його змінні будуть незмінними для будь-яких викликів.

Коли можна використовувати це?


Якщо потрібно перевести який-небудь специфічний примірник з одного класу в інший, то можна використовувати шаблон «Одинак», щоб усунути проведення цього примірника через конструктор або аргумент. Припустимо, ви створили клас 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 (Шаблони проектування) містить багато інформації про це. Якщо цього недостатньо, то ви завжди можете прочитати книгу «Design Patterns: Elements of Reusable Object-Oriented Software» («Шаблони проектування: елементи багаторазово використовується об'єктно-орієнтованого програмного забезпечення»), яка вважається однією з кращих по розглянутій темі.

І останнє: використовуючи ці шаблони проектування, завжди перевіряйте, що ви намагаєтеся вирішити правильно поставлене завдання. Як я згадував раніше, ці шаблони проектування необхідно використовувати дуже обережно: вони — при використанні в неналежному контексті — можуть погіршити ситуацію, але при правильному використанні вони просто життєво необхідні.

Якщо ви знайшли цю статтю корисною, то чому б не ознайомитися з пропозицією PHP-скриптів на Envato Market. Там представлені тисячі корисних скриптів, які можуть прискорити вашу розробку і покращити кінцевий результат. Є системи бронювання, контактні форми AJAX, системи інформаційних бюлетенів та багато іншого.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.