Краткое изложение шаблонов проектирования GoF

Намерение

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

Также известен как

Виртуальный конструктор

Мотивация

Framework использует абстрактные классы для определения и поддержания отношений между объектами. Фреймворк также часто отвечает за создание этих объектов.

Рассмотрим структуру для приложений, которые могут представлять пользователю несколько документов. Двумя ключевыми абстракциями в этой структуре являются классы Application и Document. Оба класса являются абстрактными, и клиенты должны создавать их подклассы, чтобы реализовать свои реализации для конкретных приложений. Класс Application отвечает за управление документами и будет создавать их по мере необходимости.

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

Паттерн Factory Method предлагает решение. Он инкапсулирует знания о том, какой подкласс Document создать, и перемещает эти знания за рамки.

Подкласс Application переопределяет абстрактную операцию CreateDocument в Application, чтобы вернуть соответствующий подкласс Document. После создания экземпляра подкласса Application он может создавать экземпляры документов, специфичных для приложения, не зная их класса. Мы называем CreateDocument фабричным методом, поскольку он отвечает за «производство» объекта.

Применимость

Используйте шаблон Factory Method, когда

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

Состав

Участники

Продукт

(Документ)

  • определяет интерфейс объектов, которые создает фабричный метод.

БетонПродукт

(МойДокумент)

  • реализует интерфейс Продукта

Создатель

(Приложение)

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

ConcreteCreator

(Мое приложение)

  • переопределяет фабричный метод для возврата экземпляра ConcreteProduct.

Сотрудничество

  • Creator полагается на свои подклассы для определения фабричного метода, чтобы он возвращал экземпляр соответствующего ConcreteProduct.

Последствия

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

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

Вот два дополнительных следствия паттерна Factory Method:

  1. Предоставляет крючки для подклассов. Создание объектов внутри класса с помощью фабричного метода всегда более гибко, чем создание объекта напрямую. Фабричный метод дает подклассам ловушку для предоставления расширенной версии объекта.
  2. Соединяет параллельные иерархии классов. Параллельные иерархии классов возникают, когда класс делегирует часть своих обязанностей отдельному классу. Рассмотрите графические фигуры, которыми можно манипулировать в интерактивном режиме; то есть их можно растягивать, перемещать или вращать с помощью мыши. Реализация таких взаимодействий не всегда проста. Часто требуется хранение и обновление информационных записей о состоянии манипуляции. Это состояние необходимо только во время манипуляций.

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

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

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

Выполнение

Учтите следующую проблему при применении шаблона Factory Method:

  1. Две разновидности. Двумя основными вариантами шаблона фабричного метода являются (1) случай, когда класс Creator является абстрактным классом и не обеспечивает реализацию объявляемого им фабричного метода, и (2) случай, когда Creator конкретный класс и предоставить реализацию по умолчанию для фабричного метода. В первом случае требуются подклассы для определения реализации, потому что не существует разумного значения по умолчанию. Во втором случае конкретный Creator использует фабричный метод прежде всего для гибкости.
  2. Параметрированные фабричные методы. Еще один вариант шаблона позволяет фабричному методу создавать несколько видов продуктов. Фабричный метод принимает параметр, определяющий тип создаваемого объекта. Все объекты, создаваемые фабричным методом, будут иметь общий интерфейс Product.
  3. Варианты и проблемы, зависящие от языка. Различные языки допускают другие интересные варианты и оговорки. Программы на языке Smalltalk часто используют метод, возвращающий класс объекта, экземпляр которого нужно создать. Фабричный метод Creator может использовать это значение для создания продукта, а ConcreteCreator может сохранять или даже вычислять это значение. Результатом является еще более поздняя привязка для типа ConcreteProduct, который должен быть создан.
  4. Использование шаблонов во избежание создания подклассов. Еще одна потенциальная проблема с фабричными методами заключается в том, что они могут заставить вас создать подкласс только для создания соответствующих объектов Product. Другой способ обойти это в C++ — предоставить шаблонный подкласс Creator, параметризованный классом Product.
  5. Соглашения об именах. Хорошей практикой является использование соглашений об именах, которые дают понять, что вы используете фабричные методы.

Образец кода

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

вот🔗!

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

Сначала мы определим фабричные методы в MazeGame для создания объектов лабиринта, комнаты, стены и двери:

import commoncode.*;

public class MazeGame_Factory {
  
  public MazeGame_Factory() {}
  
  public Maze MakeMaze() 
    { return new Maze(); }
  
  public Wall MakeWall() 
    { return new Wall(); }
  
  public Room MakeRoom(int n) 
    { return new Room(n); }
  
  public Door MakeDoor(Room r1, Room r2)
    { return new Door(r1, r2); }
  
  public Maze CreateMaze() {
    Maze aMaze   = this.MakeMaze();
    Room r1      = this.MakeRoom(1);
    Room r2      = this.MakeRoom(2);
    Door theDoor = this.MakeDoor(r1, r2);

    r1.SetSide(Direction.North, this.MakeWall());
    r1.SetSide(Direction.East,  theDoor);
    r1.SetSide(Direction.South, this.MakeWall());
    r1.SetSide(Direction.West,  this.MakeWall());

    r2.SetSide(Direction.North, this.MakeWall());
    r2.SetSide(Direction.East,  this.MakeWall());
    r2.SetSide(Direction.South, this.MakeWall());
    r2.SetSide(Direction.West,  theDoor);

    aMaze.AddRoom(r1);
    aMaze.AddRoom(r2);

    return aMaze;
  }

}

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

Теперь нам нужно переписать CreateMaze, чтобы использовать эти фабричные методы.

Известные виды использования

Фабричные методы пронизывают инструментальные средства и платформы. Пример предыдущего документа является типичным использованием в MacApp и ET++ [WGM88]. Пример манипулятора от Unidraw.

Представление класса в структуре Модель/Представление/Контроллер Smalltalk-80 имеет метод defaultController, который создает контроллер, и это может показаться фабричным методом [Par90]. Но подклассы Views определяют класс своего контроллера по умолчанию, определяя defaultControllerClass, который возвращает класс, из которого defaultController создает экземпляры. Таким образом, defaultControllerClass — это настоящий фабричный метод, tKat — это метод, который подклассы должны переопределять.

Связанные шаблоны

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

Фабричные методы обычно вызываются внутри шаблонных методов.

Прототипы не требуют создания подкласса Creator. Однако для них часто требуется операция Initialize в классе Product. Creator использует Initialize для инициализации объекта. Фабричный метод не требует такой операции.