Абстрактная фабрика (шаблон проектирования)

Абстрактная фабрика (Abstract Factory)

Википедия гласит:


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

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

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

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

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

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

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

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

Примеры на Java и Python.

Проблема

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

Игра отлично работала на вашем мощном компьютере. Однако ваш друг сообщил, что игра начинает тормозить и вылетает через несколько минут после запуска. Покопавшись в логах, вы обнаружили, что игра вылетает из-за недостатка оперативной памяти. У вашего друга компьютер значительно менее «прокачанный», поэтому проблема у него и проявляется так быстро.

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

.NET Optimized code in C#

The .NET optimized code demonstrates the same real-world situation as above but uses modern, built-in .NET features, such as, generics, reflection, LINQ, lambda functions, and more.

You can find an example on our pattern page. All other patterns, and so much more, are available in our Dofactory .NET product.

Dofactory .NET includes the Gang of Four and Enterprise patterns, but also many other innovations including our Ultra-Clean Architecture, powerful low-code tactics, Rapid Application Development (RAD) techniques, and much more.

Accelerate your application development to where you can write entire solutions in just 33 days!. This unique package will change your outlook on development and your career.  Here’s what is included:

Dofactory .NET.NET Developer Pack Learn More

  • 69 gang-of-four pattern projects
  • 46 head-first pattern projects
  • Fowler’s enterprise patterns
  • Multi-tier patterns
  • Convention over configuration
  • Active Record and CQRS patterns
  • Repository and Unit-of-Work patterns
  • MVC, MVP, & MVVM patterns
  • SparkTM Rapid App Dev (RAD) data access
  • Complete Analytics, Dashboard App
  • Complete Art Shop, Ecommerce App
  • Complete SaaS, Multi-Tenant App
  • Complete CRM, Customer Relationship App
  • 33-Day App Factory
  • Everything 100% source code

  Previous  

  Next  

Строитель (Builder)

Википедия гласит:

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

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

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

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

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

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

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

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

Примеры на Java и Python.

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

Паттерн проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Давайте рассмотрим один из наиболее часто используемых порождающих паттернов — Фабричный метод (Factory Method).

Смотрите моё видео на тему Фабричный метод C# — Factory Method C#

Как я уже писал ранее, существует три вида паттернов проектирования:

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

Фабричный метод (Factory Method) — это порождающий паттерн, который задает интерфейс создания экземпляра объекта, но при этом позволяет наследникам решать экземпляр какого типа создавать. То есть, базовый класс определяет интерфейс создания экземпляра, а реализацию процесса инстанцирования предоставляет наследникам.

Structural code in C#

This structural code demonstrates the Factory method offering great flexibility in creating different objects. The Abstract class may provide a default object, but each subclass can instantiate an extended version of the object.

using System;

namespace DoFactory.GangOfFour.Factory.Structural

{

  ///<summary>

  /// MainApp startup class for Structural

  /// Factory Method Design Pattern.

  ///</summary>

  class MainApp

  {

    ///<summary>

    /// Entry point into console application.

    ///</summary>

    static void Main()

    {

      // An array of creators

      Creator[] creators = new Creator;

      creators = new ConcreteCreatorA();

      creators = new ConcreteCreatorB();

      // Iterate over creators and create products

      foreach (Creator creator in creators)

      {

        Product product = creator.FactoryMethod();

        Console.WriteLine(«Created {0}»,

          product.GetType().Name);

      }

      // Wait for user

      Console.ReadKey();

    }

  }

  ///<summary>

  /// The ‘Product’ abstract class


  ///</summary>

  abstract class Product

  {

  }

  ///<summary>

  /// A ‘ConcreteProduct’ class

  ///</summary>

  class ConcreteProductA : Product

  {

  }

  ///<summary>

  /// A ‘ConcreteProduct’ class

  ///</summary>

  class ConcreteProductB : Product

  {

  }

  ///<summary>

  /// The ‘Creator’ abstract class

  ///</summary>

  abstract class Creator

  {

    public abstract Product FactoryMethod();

  }

  ///<summary>

  /// A ‘ConcreteCreator’ class

  ///</summary>

  class ConcreteCreatorA : Creator

  {

    public override Product FactoryMethod()

    {

      return new ConcreteProductA();

    }

  }

  ///<summary>

  /// A ‘ConcreteCreator’ class

  ///</summary>

  class ConcreteCreatorB : Creator

  {

    public override Product FactoryMethod()

    {

      return new ConcreteProductB();

    }

  }

}

Решение

Паттерн Шаблонный метод предлагает разбить алгоритм на последовательность шагов, описать эти шаги в отдельных методах и вызывать их в одном шаблонном методе друг за другом.

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

В нашем примере с дата-майнингом мы можем создать общий базовый класс для всех трёх алгоритмов. Этот класс будет состоять из шаблонного метода, который последовательно вызывает шаги разбора документов.

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

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

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

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

Концептуальный пример

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

Program.cs: Пример структуры паттерна



using System;

namespace RefactoringGuru.DesignPatterns.FactoryMethod.Conceptual { // Класс Создатель объявляет фабричный метод, который должен возвращать // объект класса Продукт

Подклассы Создателя обычно предоставляют // реализацию этого метода. abstract class Creator { // Обратите внимание, что Создатель может также обеспечить реализацию // фабричного метода по умолчанию. public abstract IProduct FactoryMethod();

// Также заметьте, что, несмотря на название, основная обязанность // Создателя не заключается в создании продуктов. Обычно он содержит // некоторую базовую бизнес-логику, которая основана на объектах // Продуктов, возвращаемых фабричным методом

Подклассы могут косвенно // изменять эту бизнес-логику, переопределяя фабричный метод и возвращая // из него другой тип продукта. public string SomeOperation() { // Вызываем фабричный метод, чтобы получить объект-продукт. var product = FactoryMethod(); // Далее, работаем с этим продуктом. var result = «Creator: The same creator’s code has just worked with » + product.Operation();

return result; } }

// Конкретные Создатели переопределяют фабричный метод для того, чтобы // изменить тип результирующего продукта. class ConcreteCreator1 : Creator { // Обратите внимание, что сигнатура метода по-прежнему использует тип // абстрактного продукта, хотя фактически из метода возвращается // конкретный продукт. Таким образом, Создатель может оставаться // независимым от конкретных классов продуктов. public override IProduct FactoryMethod() { return new ConcreteProduct1(); } }

class ConcreteCreator2 : Creator { public override IProduct FactoryMethod() { return new ConcreteProduct2(); } }

// Интерфейс Продукта объявляет операции, которые должны выполнять все // конкретные продукты. public interface IProduct { string Operation(); }

// Конкретные Продукты предоставляют различные реализации интерфейса // Продукта. class ConcreteProduct1 : IProduct { public string Operation() { return «{Result of ConcreteProduct1}»; } }

class ConcreteProduct2 : IProduct { public string Operation() { return «{Result of ConcreteProduct2}»; } }

class Client { public void Main() { Console.WriteLine(«App: Launched with the ConcreteCreator1.»); ClientCode(new ConcreteCreator1()); Console.WriteLine(«»);

Console.WriteLine(«App: Launched with the ConcreteCreator2.»); ClientCode(new ConcreteCreator2()); }

// Клиентский код работает с экземпляром конкретного создателя, хотя и // через его базовый интерфейс. Пока клиент продолжает работать с // создателем через базовый интерфейс, вы можете передать ему любой // подкласс создателя. public void ClientCode(Creator creator) { // … Console.WriteLine(«Client: I’m not aware of the creator’s class,» + «but it still works.\n» + creator.SomeOperation()); // … } }

class Program { static void Main(string[] args) { new Client().Main(); } } }

Output.txt: Результат выполнения

App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of ConcreteProduct2}

.NET Optimized code in C#

The .NET optimized code demonstrates the same real-world situation as above but uses modern, built-in .NET features, such as, generics, reflection, LINQ, lambda functions, and more.

You can find an example on our pattern page. All other patterns, and so much more, are available in our Dofactory .NET product.

Dofactory .NET includes the Gang of Four and Enterprise patterns, but also many other innovations including our Ultra-Clean Architecture, powerful low-code tactics, Rapid Application Development (RAD) techniques, and much more.

Accelerate your application development to where you can write entire solutions in just 33 days!. This unique package will change your outlook on development and your career.  Here’s what is included:

Dofactory .NET.NET Developer Pack Learn More

  • 69 gang-of-four pattern projects
  • 46 head-first pattern projects
  • Fowler’s enterprise patterns
  • Multi-tier patterns
  • Convention over configuration
  • Active Record and CQRS patterns
  • Repository and Unit-of-Work patterns
  • MVC, MVP, & MVVM patterns
  • SparkTM Rapid App Dev (RAD) data access
  • Complete Analytics, Dashboard App
  • Complete Art Shop, Ecommerce App
  • Complete SaaS, Multi-Tenant App
  • Complete CRM, Customer Relationship App
  • 33-Day App Factory
  • Everything 100% source code

  Previous  

  Next  

Статический фабричный метод

Статический фабричный метод — вариация создающего метода, объявленная как . Если этот метод создаёт объекты своего же класса, то, по сути, он выступает в роли альтернативного конструктора. Это может быть полезно, если:

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

  • Хочется повторно использовать готовые объекты, вместо создания новых (например, паттерн Одиночка). При вызове конструктора вы всегда создаёте новый объект. Это можно обойти, если вынести вызов конструктора в новый метод. В этом методе вы можете сначала поискать готовый объект в каком-то кеше, и только если его нет, создать новый объект.

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

class User {
    private $id, $name, $email, $phone;

    public function __construct($id, $name, $email, $phone) {
        $this->id = $id;
        $this->name = $name;
        $this->email = $email;
        $this->phone = $phone;
    }

    public static function load($id) {
        list($id, $name, $email, $phone) = DB::load_data('users', 'id', 'name', 'email', 'phone');
        $user = new User($id, $name, $email, $phone);
        return $user;
    }
}

Structural code in C#

This structural code demonstrates the Abstract Factory pattern creating parallel hierarchies of objects. Object creation has been abstracted and there is no need for hard-coded class names in the client code.

using System;

namespace DoFactory.GangOfFour.Abstract.Structural

{


  ///<summary>

  /// MainApp startup class for Structural

  /// Abstract Factory Design Pattern.

  ///</summary>

  class MainApp

  {

    ///<summary>

    /// Entry point into console application.

    ///</summary>

    public static void Main()

    {

      // Abstract factory #1

      AbstractFactory factory1 = new ConcreteFactory1();

      Client client1 = new Client(factory1);

      client1.Run();

      // Abstract factory #2

      AbstractFactory factory2 = new ConcreteFactory2();

      Client client2 = new Client(factory2);

      client2.Run();

      // Wait for user input

      Console.ReadKey();

    }

  }

  ///<summary>

  /// The ‘AbstractFactory’ abstract class

  ///</summary>

  abstract class AbstractFactory

  {

    public abstract AbstractProductA CreateProductA();

    public abstract AbstractProductB CreateProductB();

  }

  ///<summary>

  /// The ‘ConcreteFactory1’ class

  ///</summary>

  class ConcreteFactory1 : AbstractFactory

  {

    public override AbstractProductA CreateProductA()

    {

      return new ProductA1();

    }

    public override AbstractProductB CreateProductB()

    {

      return new ProductB1();

    }

  }

  ///<summary>

  /// The ‘ConcreteFactory2’ class

  ///</summary>

  class ConcreteFactory2 : AbstractFactory

  {

    public override AbstractProductA CreateProductA()

    {

      return new ProductA2();

    }

    public override AbstractProductB CreateProductB()

    {

      return new ProductB2();

    }

  }

  ///<summary>

  /// The ‘AbstractProductA’ abstract class

  ///</summary>

  abstract class AbstractProductA


  {

  }

  ///<summary>

  /// The ‘AbstractProductB’ abstract class

  ///</summary>

  abstract class AbstractProductB

  {

    public abstract void Interact(AbstractProductA a);

  }

  ///<summary>

  /// The ‘ProductA1’ class

  ///</summary>

  class ProductA1 : AbstractProductA

  {

  }

  ///<summary>

  /// The ‘ProductB1’ class

  ///</summary>

  class ProductB1 : AbstractProductB

  {

    public override void Interact(AbstractProductA a)

    {

      Console.WriteLine(this.GetType().Name +

        » interacts with » + a.GetType().Name);

    }

  }

  ///<summary>

  /// The ‘ProductA2’ class

  ///</summary>

  class ProductA2 : AbstractProductA

  {

  }

  ///<summary>

  /// The ‘ProductB2’ class

  ///</summary>

  class ProductB2 : AbstractProductB

  {

    public override void Interact(AbstractProductA a)

    {

      Console.WriteLine(this.GetType().Name +

        » interacts with » + a.GetType().Name);

    }

  }

  ///<summary>

  /// The ‘Client’ class. Interaction environment for the products.

  ///</summary>

  class Client

  {

    private AbstractProductA _abstractProductA;

    private AbstractProductB _abstractProductB;

    // Constructor

    public Client(AbstractFactory factory)

    {

      _abstractProductB = factory.CreateProductB();

      _abstractProductA = factory.CreateProductA();

    }

    public void Run()

    {

      _abstractProductB.Interact(_abstractProductA);

    }

  }

}


С этим читают