Порождающие паттерны: одиночка, фабричный метод, абстрактная фабрика, строитель, прототип, пул объектов. - Painted-Black/BMSTU-OOP GitHub Wiki

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

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

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

Используйте паттерн абстрактная фабрика, когда:

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

Участники

AFactory

  • AbstractFactory - абстрактная фабрика
    • объявляет интерфейс для операций, создающих абстрактные объекты-продукты
  • ConcreteFactory - конкретная фабрика
    • реализует операции, создающие конкретные объекты-продукты
  • AbstractProduct - абстрактный продукт
    • объявляет интерфейс для типа объекта-продукта
  • ConcreteProduct - конкретный продукт
    • определяет объект-продукт, создаваемый соответствующей конкретной фабрикой
    • реализует интерфейс AbstractProduct
  • Client - клиент
    • пользуется исключительно интерфейсами, которые объявлены в классах AbstractFactory и AbstractProduct.

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

AbstractFactory передоверяет создание объектов-продуктов своему подклассу ConcreteFactory.

Патерн абстрактная фабрика имеет следующие плюсы и минусы:

  • изолирует конкретные классы
  • упрощает замену семейств продуктов
  • гарантирует сочетаемость продуктов
  • поддержать новый вид продуктов трудно.

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

Используйте паттерн строитель, когда:

  • алгоритм создания сложного объекта не должен зависеть от того, из каких частей состоит объект и как они стыкуются между собой
  • процесс конструирования объекта должен обеспечивать различные представления конструируемого объекта

Участники

Builder

  • Builder
    • задает абстрактный интерфейс для создания частей объекта Product
  • ConcreteBuilder
    • конструирует и собирает вместе части продукта посредством реализации интерфейса Builder
    • определяет создаваемое представление и следит за ним
    • предоставляет интерфейс для доступа к продукту
  • Director - распорядитель
    • конструирует объект, пользуясь интерфейсом Builder
  • Product
    • представляет сложный конструируемый объект. ConcreteBuilder строит внетреннее представление продукта и определяет процесс его сборки
    • включает классы, которые определяют составные части, в том числе интерфейсы для сборки конечного результата из частей.

Клиент создает объект-распорядитель Director и конфигурирует его нужным объектом-строителем Builder. Распорядитель уведомляет строителя о том, что нужно построить очередную часть продукта. Строитель обрабатывает запросы распорядителя и добавляет новые части к продукту. Клиент забирает продукт у строителя.

Плюсы и минусы паттерна строитель:

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

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

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

Используйте паттерн фабричный метод, когда:

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

Участники

FactoryMethod

  • Product
    • определяет интерфейс объектов, создаваемых фабричным методом
  • ConcreteProduct
    • реализует интерфейс Product
  • Creator
    • объявляет фабричный метод, возвращающий объекты типа Product. Creator может также определять реалзацию по умолчанию фабричноно метода, который возвращает объект ConcreteProduct
    • может вызывать фабричный метод для создания объекита Product
  • ConcreteCreator
    • замещает фабричный метод, возвращающий объект ConcreteProduct.

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

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

class Creator
{
    public:
        virtual unique_ptr<Product> create() = 0;
};

template <typename Tprod>
class ConCreator: public Creator
{
    virtual unique_ptr<Product> create() override;
};

unique_ptr<Product> ConCreator<TProd>::create()
{
    return unique_ptr(new Tprod());
}
//...
ConCreator<ConProd> cr;
//...
unique_ptr<Product> ptr = create();

Задает виды создаваемых объектов с помощью экземпляра-прототипа и создает новые объекты путем копирования этого прототипа.

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

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

Участники

Prototype

  • Prototype
    • обяляет интерфейс для клонирования самого себя
  • ConcretePrototype
    • реализует операцию клонирования себя
  • Client
    • создает новый объект, обращаясь к прототипу с запросом клонировать себя.

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

Преимущества:

  • добавление и удаление продуктов во время выполения
  • спецификация новых объектов путем изменения значений
  • специфицирование новых объектов путем изменения структуры
  • уменьшение числа подклассов
  • динамическое конфигурирование приложения классами.

Одиночка

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

Используйте паттер одиночка, когда:

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

Участники

Simgleton

  • Singleton
    • определяет операцию Instance, которая позволяет клиентам получать доступ к единственноу экземпляру. Instance - это операция класса
    • может нести ответственность за содание собственного уникального экземпляра.

Клиенты получают доступ к экземпляру Singleton только через операцию Instance.

Достоинства:

  • контролирует доступ к единственному экземпляру

  • уменьшение числа имен (позволяет избежать засорения пространства имен глобальными переменными)

  • допускает уточнение операций и представления

  • допускает переменное число экземпляров

  • большая гибкость, чем у операций класса

    template class Singleton { public: static Type& instance() { if (MyInstance == nullptr) MyInstance = new Type(); return MyInstance; } Singleton() = delete; private: static Type* MyInstance = nullptr; };

Недостатки:

  • глобальный объект
  • мы не можем управлять созданием и удалением объекта.

Альтернатива: Creator с get.

Пул объектов

Модификация Creator

template <typename Type>
class ObjectPool
{
    public:
        shared_ptr<Type> create();
        vector<shared_ptr<Type>> vec;
};

template <typename Type>
shared_ptr<Type> ObjectPool<Type>::get()
{
    bool key = false;
    size_t i = 0;
    while (i < vec.size() && !key)
    {
        if (vec[i].use_count() == 0)
            key = true;
        else
            i++;
    }
    return key ? vec[i].create();
}
⚠️ **GitHub.com Fallback** ⚠️