Что нового
  • Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Шаблоны Проектирования Простым Языком. Часть Первая. Порождающие Шаблоны

Sascha

Заместитель Администратора
Команда форума
Администратор
Регистрация
9 Май 2015
Сообщения
1,071
Баллы
155
Возраст
51
Рассказывает

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.



Шаблоны проектирования — это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками, как решать определенные проблемы в определенных ситуациях.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

их следующим образом:


Шаблон проектирования, или паттерн, в разработке программного обеспечения — повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования, в рамках некоторого часто возникающего контекста.
Будьте осторожны


Учтите, что:

  • шаблоны проектирования не являются решением всех ваших проблем;
  • не пытайтесь насильно использовать их, из-за этого могут произойти плохие вещи. Шаблоны — решения проблем, а не решения для поиска проблем;
  • если их правильно использовать в нужных местах, то они могут стать спасением, а иначе могут привести к ужасному беспорядку.

Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.

Типы шаблонов


Шаблоны бывают следующих трех видов:

  1. Порождающие.
  2. Структурные.
  3. Поведенческие.
Порождающие шаблоны проектирования


Если говорить простыми словами, то это шаблоны, которые предназначены для создания экземпляра объекта или группы связанных объектов.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


Порождающие шаблоны — шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Шаблон, порождающий классы, использует наследование, чтобы изменять наследуемый класс, а шаблон, порождающий объекты, делегирует инстанцирование другому объекту.

Существуют следующие порождающие шаблоны:



Простая фабрика


Пример из жизни:

Представьте, что вам надо построить дом, и вам нужны двери. Было бы глупо каждый раз, когда вам нужны двери, надевать вашу столярную форму и начинать делать дверь. Вместо этого вы делаете её на фабрике.

Простыми словами:

Простая фабрика генерирует сущность для клиента, не раскрывая никакой логики.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


В объектно-ориентированном программировании (ООП), фабрика — это объект для создания других объектов. Формально фабрика — это функция или метод, который возвращает объекты изменяющегося прототипа или класса из некоторого вызова метода, который считается «новым».
Пример в коде


Сначала у нас есть интерфейс двери и реализация:

interface Door
{
public function getWidth(): float;
public function getHeight(): float;
}

class WoodenDoor implements Door
{
protected $width;
protected $height;

public function __construct(float $width, float $height)
{
$this->width = $width;
$this->height = $height;
}

public function getWidth(): float
{
return $this->width;
}

public function getHeight(): float
{
return $this->height;
}
}

Затем у нас есть наша фабрика дверей, которая делает дверь и возвращает её:

class DoorFactory
{
public static function makeDoor($width, $height): Door
{
return new WoodenDoor($width, $height);
}
}

И затем мы можем использовать всё это:

$door = DoorFactory::makeDoor(100, 200);
echo 'Width: ' . $door->getWidth();
echo 'Height: ' . $door->getHeight();

Когда использовать?


Когда создание объекта — это не просто несколько присвоений, а какая-то логика, тогда имеет смысл создать отдельную фабрику вместо повторения одного и того же кода повсюду.

Фабричный метод


Пример из жизни:

Рассмотрим пример с менеджером по найму. Невозможно одному человеку провести собеседования со всеми кандидатами на все вакансии. В зависимости от вакансии, он должен распределить этапы собеседования между разными людьми.

Простыми словами:

Менеджер предоставляет способ передать логику экземпляра дочерним классам.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


Фабричный метод — порождающий шаблон проектирования, предоставляющий подклассам интерфейс для создания экземпляров некоторого класса. В момент создания наследники могут определить, какой класс создавать. Иными словами, данный шаблон делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не специфические классы, а манипулировать абстрактными объектами на более высоком уровне.
Пример в коде


Возьмем приведенный выше пример про менеджера. Изначально у нас есть интерфейс интервьюера и несколько реализаций для него:

interface Interviewer
{
public function askQuestions();
}

class Developer implements Interviewer
{
public function askQuestions()
{
echo 'Спрашивает про шаблоны проектирования!';
}
}

class CommunityExecutive implements Interviewer
{
public function askQuestions()
{
echo 'Спрашивает о работе с сообществом';
}
}

Теперь создадим нашего менеджера по найму:

abstract class HiringManager
{

// Фабричный метод
abstract public function makeInterviewer(): Interviewer;

public function takeInterview()
{
$interviewer = $this->makeInterviewer();
$interviewer->askQuestions();
}
}

И теперь любой дочерний класс может расширять его и предоставлять необходимого интервьюера:

class DevelopmentManager extends HiringManager
{
public function makeInterviewer(): Interviewer
{
return new Developer();
}
}

class MarketingManager extends HiringManager
{
public function makeInterviewer(): Interviewer
{
return new CommunityExecutive();
}
}

Пример использования:

$devManager = new DevelopmentManager();
$devManager->takeInterview(); // Вывод: Спрашивает о шаблонах проектирования!

$marketingManager = new MarketingManager();
$marketingManager->takeInterview(); // Вывод: Спрашивает о работе с сообществом

Когда использовать?


Полезен, когда есть некоторая общая обработка в классе, но необходимый подкласс динамически определяется во время выполнения. Иными словами, когда клиент не знает, какой именно подкласс ему может понадобиться.

Абстрактная фабрика


Пример из жизни:

Расширим наш пример про двери из простой фабрики. В зависимости от ваших нужд, вам понадобится деревянная дверь из одного магазина, железная дверь — из другого или пластиковая — из третьего. Кроме того, вам понадобится соответствующий специалист: столяр для деревянной двери, сварщик для железной двери и так далее. Как вы можете заметить, тут есть зависимость между дверьми.

Простыми словами:

Фабрика фабрик. Фабрика, которая группирует индивидуальные, но связанные/зависимые фабрики без указания их конкретных классов.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


Абстрактная фабрика — порождающий шаблон проектирования, предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов. Шаблон реализуется созданием абстрактного класса Factory, который представляет собой интерфейс для создания компонентов системы (например, для оконного интерфейса он может создавать окна и кнопки). Затем пишутся классы, реализующие этот интерфейс.
Пример в коде


Используем пример про двери. Сначала у нас есть интерфейс нашей двери и несколько его реализаций:

interface Door
{
public function getDescription();
}

class WoodenDoor implements Door
{
public function getDescription()
{
echo 'Я деревянная дверь';
}
}

class IronDoor implements Door
{
public function getDescription()
{
echo 'Я железная дверь';
}
}

Затем у нас есть несколько подходящих экспертов для каждого типа дверей:

interface DoorFittingExpert
{
public function getDescription();
}

class Welder implements DoorFittingExpert
{
public function getDescription()
{
echo 'Я работаю только с железными дверьми';
}
}

class Carpenter implements DoorFittingExpert
{
public function getDescription()
{
echo 'Я работаю только с деревянными дверьми';
}
}

Теперь у нас есть наша абстрактная фабрика, которая позволит нам создать семейство связанных объектов. То есть фабрика деревянных дверей предоставит нам деревянную дверь и эксперта по деревянным дверям. Аналогично для железных дверей:

interface DoorFactory
{
public function makeDoor(): Door;
public function makeFittingExpert(): DoorFittingExpert;
}

// Деревянная фабрика вернет деревянную дверь и столяра
class WoodenDoorFactory implements DoorFactory
{
public function makeDoor(): Door
{
return new WoodenDoor();
}

public function makeFittingExpert(): DoorFittingExpert
{
return new Carpenter();
}
}

// Железная фабрика вернет железную дверь и сварщика
class IronDoorFactory implements DoorFactory
{
public function makeDoor(): Door
{
return new IronDoor();
}

public function makeFittingExpert(): DoorFittingExpert
{
return new Welder();
}
}

Пример использования:

$woodenFactory = new WoodenDoorFactory();

$door = $woodenFactory->makeDoor();
$expert = $woodenFactory->makeFittingExpert();

$door->getDescription(); // Вывод: Я деревянная дверь
$expert->getDescription(); // Вывод: Я работаю только с деревянными дверями

// Аналогично для железной двери
$ironFactory = new IronDoorFactory();

$door = $ironFactory->makeDoor();
$expert = $ironFactory->makeFittingExpert();

$door->getDescription(); // Вывод: Я железная дверь
$expert->getDescription(); // Вывод: Я работаю только с железными дверями

Как вы можете заметить, фабрика деревянных дверей инкапсулирует столяра и деревянную дверь, а фабрика железных дверей инкапсулирует железную дверь и сварщика. Это позволило нам убедиться, что для каждой двери мы получим нужного нам эксперта.

Когда использовать?


Когда есть взаимосвязанные зависимости с не очень простой логикой создания.

Строитель


Пример из жизни:

Представьте, что вы пришли в McDonalds и заказали конкретный продукт, например, БигМак, и вам готовят его без лишних вопросов. Это пример простой фабрики. Но есть случаи, когда логика создания может включать в себя больше шагов. Например, вы хотите индивидуальный сендвич в Subway: у вас есть несколько вариантов того, как он будет сделан. Какой хлеб вы хотите? Какие соусы использовать? Какой сыр? В таких случаях на помощь приходит шаблон «Строитель».

Простыми словами:

Шаблон позволяет вам создавать различные виды объекта, избегая засорения конструктора. Он полезен, когда может быть несколько видов объекта или когда необходимо множество шагов связанных с его созданием.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


Строитель — порождающий шаблон проектирования, который предоставляет способ создания составного объекта. Предназначен для решения проблемы антипаттерна «Телескопический конструктор».

Давайте я покажу на примере, что такое «Телескопический конструктор». Когда-то мы все видели конструктор вроде такого:

public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true)
{
}

Как вы можете заметить, количество параметров конструктора может резко увеличиться, и станет сложно понимать расположение параметров. Кроме того, этот список параметров будет продолжать расти, если вы захотите добавить новые варианты. Это и есть «Телескопический конструктор».

Пример в коде


Адекватной альтернативой будет использование шаблона «Строитель». Сначала у нас есть бургер, который мы хотим создать:

class Burger
{
protected $size;

protected $cheese = false;
protected $pepperoni = false;
protected $lettuce = false;
protected $tomato = false;

public function __construct(BurgerBuilder $builder)
{
$this->size = $builder->size;
$this->cheese = $builder->cheese;
$this->pepperoni = $builder->pepperoni;
$this->lettuce = $builder->lettuce;
$this->tomato = $builder->tomato;
}
}

Затем мы берём «Строителя»:

class BurgerBuilder
{
public $size;

public $cheese = false;
public $pepperoni = false;
public $lettuce = false;
public $tomato = false;

public function __construct(int $size)
{
$this->size = $size;
}

public function addPepperoni()
{
$this->pepperoni = true;
return $this;
}

public function addLettuce()
{
$this->lettuce = true;
return $this;
}

public function addCheese()
{
$this->cheese = true;
return $this;
}

public function addTomato()
{
$this->tomato = true;
return $this;
}

public function build(): Burger
{
return new Burger($this);
}
}

Пример использования:

$burger = (new BurgerBuilder(14))
->addPepperoni()
->addLettuce()
->addTomato()
->build();

Когда использовать?


Когда может быть несколько видов объекта и надо избежать «телескопического конструктора». Главное отличие от «фабрики» — это то, что последний используется, когда создание занимает один шаг, а «строитель» применяется при множестве шагов.

Прототип


Пример из жизни:

Помните Долли? Овечка, которая была клонирована. Не будем углубляться, главное — это то, что здесь все вращается вокруг клонирования.

Простыми словами:

Прототип создает объект, основанный на существующем объекте при помощи клонирования.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа. Он позволяет уйти от реализации и позволяет следовать принципу «программирование через интерфейсы». В качестве возвращающего типа указывается интерфейс / абстрактный класс на вершине иерархии, а классы-наследники могут подставить туда наследника, реализующего этот тип.

Проще говоря, это паттерн создания объекта через клонирование другого объекта вместо создания его, используя конструктор.

Короче говоря, он позволяет вам создавать копию существующего объекта и модернизировать его согласно вашим нуждам, вместо того, чтобы создавать объект заново.

Пример в коде


В PHP это может быть легко реализовано с использованием clone:

class Sheep
{
protected $name;
protected $category;

public function __construct(string $name, string $category = 'Горная овечка')
{
$this->name = $name;
$this->category = $category;
}

public function setName(string $name)
{
$this->name = $name;
}

public function getName()
{
return $this->name;
}

public function setCategory(string $category)
{
$this->category = $category;
}

public function getCategory()
{
return $this->category;
}
}

Затем он может быть клонирован следующим образом:

$original = new Sheep('Джолли');
echo $original->getName(); // Джолли
echo $original->getCategory(); // Горная овечка

// Клонируем и модифицируем то что нужно
$cloned = clone $original;
$cloned->setName('Долли');
echo $cloned->getName(); // Долли
echo $cloned->getCategory(); // Горная овечка

Также вы можете использовать волшебный метод __clone для изменения клонирующего поведения.

Когда использовать?


Когда необходим объект, похожий на существующий объект, либо когда создание будет дороже клонирования.

Одиночка


Пример из жизни:

В стране одновременно может быть только один президент. Один и тот же президент должен действовать, когда того требуют обстоятельства. Президент здесь является одиночкой.

Простыми словами:

Удостоверяется, что когда-либо был создан лишь один объект определенного класса.

Википедия

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

:


Одиночка — порождающий шаблон проектирования, гарантирующий, что в однопроцессном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.

Вообще шаблон одиночка признан антипаттерном, необходимо избегать его чрезмерного использования. Он необязательно плох и может иметь полезные применения, но использовать его надо с осторожностью, потому что он вводит глобальное состояние в ваше приложение и его изменение в одном месте может повлиять на другие зоны, что вызовет трудности при отладке. Другая плохая вещь — это то, что код тесно связан с ним.

Прим. перев. Подробнее о подводных камнях шаблона одиночка читайте в

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

.

Пример в коде


Чтобы создать одиночку, сделайте конструктор приватным, отключите клонирование и создайте статичную переменную для содержания сущности:

final class President
{
private static $instance;

private function __construct()
{
// Прячем конструктор
}

public static function getInstance(): President
{
if (!self::$instance) {
self::$instance = new self();
}

return self::$instance;
}

private function __clone()
{
// Отключаем клонирование
}

private function __wakeup()
{
// Отключаем десериализацию
}
}
Пример использования:

$president1 = President::getInstance();
$president2 = President::getInstance();

var_dump($president1 === $president2); // true


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

.
 
Вверх