Вы находитесь на странице: 1из 51

Архитектура, алгоритмы и паттерны на PHP

Урок 4.
Порождающие шаблоны
На уроке разберём

● Порождающие шаблоны: общие характеристики.


● Что относят к порождающим шаблонам?
● Разбор шаблонов: Abstract Factory, Builder, Factory Method,
Prototype, Singleton.
Порождающие шаблоны
абстрагируют процесс
создания классов так, чтобы
сделать систему более
независимой от способов
создания и представления
объектов.
Список шаблонов (GoF)

Abstract Factory Builder

Factory Method Singleton Prototype


Мы рассмотрим

Abstract Factory Builder

Factory Method Singleton Prototype


Фабричный метод
(Factory Method)

Когда нужно абстрагировать


создание классов, при этом выбор
конкретного создаваемого класса
определяется на более низком
уровне..
Основная цель Фабричного метода

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


инстанцирование подклассам.
Фабричный метод: пример
interface IAdvertisement
{
public function build(array $parameters): string;
}
Имеем: генератор рекламы.
class BannerBuilder implements IAdvertisement
{
public function build(array $parameters): string
{
echo 'Возвращаем баннер';
} Хотим: получать
}
разные рекламные
class PopupBuilder implements IAdvertisement
{
блоки, не указывая их
public function build(array $parameters): string явно.
{
echo 'Возвращаем попап';
}
}
class BlogPage
{
public function getHtml(): string
{
echo 'Бизнес-логика';

$advertisement = $this->getAdvertisement()->build($this->parameters);

echo 'Бизнес-логика';
}
Описание фабричных методов
protected function getAdvertisement(): IAdvertisement
{
return new BannerBuilder();
}
}

class BlogPageWithPopup extends BlogPage


{
protected function getAdvertisement(): IAdvertisement
{
return new PopupBuilder();
}
}
Пример использования

$blogPage = new BlogPage();


$blogPage -> getHtml();

$blogPageWithPopup = new BlogPageWithPopup();


$blogPageWithPopup -> getHtml();
Плюсы паттерна

● Позволяет избавиться от зависимых от кода классов.

● Фабричный метод более гибок, чем непосредственное создание класса.

● Уменьшает связь с другими классами программы за счёт применения принципа низкой


связанности (low coupling).

● Улучшается тестируемость.

● Реализует принцип открытости / закрытости.


Минусы паттерна

● Для переопределения создаваемого класса требуется создание новых


сущностей (подклассов).
● Чем больше фабричных методов в классе, тем больше наследников для
покрытия всех возможных вариантов.
Когда стоит применять

● Классу неизвестно, объекты каких классов создавать.


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

● Фабричный метод может как иметь реализацию в конкретном классе, так


и быть абстрактным.
● Возможность получить нужный класс, прокидывая параметризированный
аргумент в фабрику.
● Может являться частью паттернов Абстрактная фабрика, Прототип
или Строитель.
Абстрактная фабрика
(Abstract Factory)

Когда нужно создать ряд


взаимосвязанных или
взаимозависимых объектов, не
указывая их конкретные классы.
UML-диаграмма
Основная цель Абстрактной фабрики

Создание семейства наборов взаимозаменяемых классов.


Абстрактная фабрика: пример
abstract class AbstractArticle
{
private $text;
public function __construct(string $text)
{
$this->text = $text;
Имеем: класс Статья в
} двух представлениях.
public function quoteSpechialChars(): string
{

}
echo 'Переводим спец. символы в html мнемоники'; Хотим: создавать
представления
abstract public function render(): string;
} Статей, не указывая
class HtmlArticle extends AbstractArticle их явно.
{
public function render(): string { echo 'Выводим статью в html'; }
}

class RssArticle extends AbstractArticle


{
public function render(): string { echo 'Выводим статью в rss'; }
}
abstract class AbstractFactory
{
abstract public function createArticle(string $content): AbstractArticle;
abstract public function createNewsFeed(array $news): AbstractNewsFeed;
}

class HtmlFactory extends AbstractFactory


{
public function createArticle(string $content): AbstractArticle
{
return new HtmlArticle($content);
}

public function createNewsFeed(array $news): AbstractNewsFeed


{
return new HtmlNewsFeed($news); Создаём фабрики
}
}

class RssFactory extends AbstractFactory


{
public function createArticle(string $content): AbstractArticle
{
return new RssArticle($content);
}

public function createNewsFeed(array $news): AbstractNewsFeed


{
return new RssNewsFeed($news);
}
}
Пример использования

class Article
{
public function createPage(AbstractFactory $abstractFactory)
{
$article = $abstractFactory->createArticle();
$article->quoteSpechialChars();
$article->render();

$newsFeed = $abstractFactory->createNewsFeed();
$newsFeed->getLastNews(10);
$newsFeed->showAsBanner();
$newsFeed->render();
}
}
Когда стоит применять

● Код должен быть независим от процесса и типов создаваемых новых


объектов.
● Когда уже внедрён Фабричный метод и требуется добавить ещё одну
группу объектов.
Плюсы паттерна

● Изолирует конкретные классы.


● Упрощает замену семейств продуктов.
● Гарантирует сочетаемость продуктов.
Минусы паттерна

● Много сил может уйти на добавление нового класса во все


наборы.
Особенности реализации

● Во время выполнения программы создаётся единственный экземпляр


конкретной фабрики.
● Абстрактная фабрика должна инициализировать конкретную фабрику
один раз в проекте.
● Реализует принцип открытости / закрытости.
Строитель (Builder)

Отделяет конструирование
сложного объекта от его
представления. В результате
одного и того же процесса
конструирования могут получаться
разные представления.
UML-диаграмма
Основная цель Строителя

Помогает конструировать сложный объект.


Строитель: пример
class Order
{
private $product;
private $delivery;
Имеем: класс с Заказом.
private $promocode;
private $warehouse;
private $mailer;

public function __construct(


Product $product,
Хотим: создавать
Delivery $delivery, объект без передачи
Warehouse $warehouse,
Mailer $mailer множества аргументов.
){
$this->product = $product;
$this->delivery = $delivery;
$this->warehouse = $warehouse;
$this->mailer = $mailer;
}
}
class Order
{
private $product;
private $delivery;
private $promocode;
private $warehouse;
private $mailer;
Вот так выглядит лучше
public function __construct(OrderBuilder $orderBuilder)
{
$this->product = $orderBuilder->getProduct();
$this->delivery = $orderBuilder->getDelivery();
$this->warehouse = $orderBuilder->getWarehouse();
$this->mailer = $orderBuilder->getMailer();
}
}
class OrderBuilder
{
private $product;
private $delivery;
private $promocode;
private $warehouse;
private $mailer;

public function setProduct(Product $product)


{ $this->product = $product; }
public function getProduct(): Product
{ return $this->product; }

public function setDelivery(Delivery $delivery)


{ $this->delivery = $delivery; }
public function getDelivery(): Delivery
{ return $this->delivery; } Класс Строитель
public function setWarehouse(Warehouse $warehouse)
{ $this->warehouse = $warehouse; }
public function getWarehouse(): Warehouse
{ return $this->warehouse; }

public function setMailer(Mailer $mailer)


{ $this->mailer = $mailer; }
public function getMailer(): Mailer
{ return $this->mailer; }

public function build(): Order


{ return new Order($this); }
}
public function testBuilder(
Product $product,
Delivery $delivery,
Warehouse $warehouse,
Mailer $mailer
){
$orderBuilder = new OrderBuilder();
$orderBuilder>setProduct($product);
Пример использования
$orderBuilder>setDelivery($delivery);
$orderBuilder>setWarehouse($warehouse);
$orderBuilder>setMailer($mailer);

$order = $orderBuilder>build();
}
Когда стоит применять

● Когда есть необходимость избавиться от конструктора с бездонным


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

● Позволяет создавать продукты пошагово.


● Даёт возможность использовать один код для разных объектов.
● Инкапсулирует часть кода.
Минусы паттерна

● Усложняет код за счёт создания новых классов.


Прототип (Prototype)

Когда нужно создать дубликат


(прототип) конкретного класса.
UML-диаграмма
Основная цель Прототипа

Клонирование объекта с теми же или похожими данными и


состоянием.
Прототип: пример
class Article
{
private $title;
private $text;
Имеем: класс Статья в
public function getTitle(): string
{ return $this->title; }
двух представлениях.
public function setTitle(string $title): void
{ $this->title = $title; }
Хотим: создавать
public function getText(): string
{ return $this->text; }
представления Статей,
не указывая их явно.
public function setText(string $text): void
{ $this->text = $text; }

public function __clone()


{/*логика клонирования*/}
}
public function testPrototype()
{
$article = new Article();
$article->setTitle('Заголовок статьи'); Создание через
$article->setText('Некоторый интересный текст');
клонирование
$article2 = clone $article;
$article2->setText('Текст другой статьи');
}
Когда стоит применять

● Когда код не должен зависеть от классов копируемых объектов.


Плюсы паттерна

● Меньше повторяющегося кода.


● Ускоряет создание объекта.
Минусы паттерна

● Сложно клонировать составные объекты.


Singleton

Когда нужно обеспечить


существование единственного
экземпляра класса.
UML-диаграмма
Одиночка: как реализовать в PHP
final class Singleton
{
private static $instance;

// Получение объекта (создаётся при первом вызове)


public static function getInstance(): Singleton
{
if (null === static::$instance) {
static::$instance = new static();
}

return static::$instance;
}

private function __construct() {/* приватный */}


private function __clone() {/* приватный */}
private function __wakeup() {/* приватный */}
}

// Создание Одиночки
Singleton::getInstance();
Когда стоит применять

● Когда в коде должен быть единственный экземпляр конкретного


класса, доступный всем клиентам.
● Когда нужно больше контроля над глобальными переменными.
Плюсы паттерна

● Гарантирует наличие единственного экземпляра класса.


Минусы паттерна

● Нарушает принцип единой ответственности класса из группы


SOLID.
● Невозможность мультипоточности.
● Затруднения при тестировании.
Особенности реализации
● Должен быть только один экземпляр некоторого класса, легко доступный
всем клиентам.
● Клиенты получают доступ к экземпляру класса Singleton только через его
операцию Instance.
● Расширение происходит путём порождения подклассов, и клиентам
нужно иметь возможность работать с расширенным экземпляром без
модификации своего кода.
● Является антипаттерном. Старайтесь использовать другие паттерны.
Задание
Разработать и реализовать на PHP собственную ORM
посредством абстрактной фабрики. Фабрики будут
реализовывать интерфейсы СУБД MySQLFactory,
PostgreSQLFactory, OracleFactory. Каждая фабрика
возвращает объекты, характерные для конкретной
СУБД. Пример компонентов:

● DBConnection — соединение с базой,

● DBRecrord — запись таблицы базы данных,

● DBQueryBuiler — конструктор запросов к базе.

Должна получиться гибкая система, позволяющая


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

Вам также может понравиться