Шаблон заводского метода - Factory method pattern

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

Обзор

Заводской метод[1]шаблон дизайна является одним из Паттерны проектирования "Банда четырех" в которых описывается, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать.

Шаблон проектирования Factory Method используется вместо обычного конструктора класса для сохранения в пределах ТВЕРДЫЙ принципы программирования, отделив построение объектов от самих объектов. Это имеет следующие преимущества и полезно, в частности, в следующих случаях:[2]

  • Позволяет создавать классы с компонентом типа, который не был заранее определен, а только определен в «интерфейсе» или который определен как динамический тип.
Так, например, класс Средство передвижения у которого есть член Мотор интерфейса IMotor, но не конкретный тип Мотор определены заранее, могут быть построены путем указания Средство передвижения конструктор для использования Электрический двигатель или БензинМотор. В Средство передвижения код конструктора затем вызывает метод фабрики Motor, чтобы создать желаемый Мотор что соответствует IMotor интерфейс.
  • Позволяет создавать подклассы для родительского элемента, тип компонента которого не был заранее определен, а только определен в интерфейсе или который определен как динамический тип.
Например, класс Средство передвижения с членом Мотор определен с динамическим типом, может иметь подклассы типа Электрический самолет и Старая машина каждый построен с другим типом двигателя. Это может быть выполнено путем создания подклассов с помощью фабричного метода Vehicle с указанием типа двигателя. В подобных случаях конструктор может быть скрыт.
  • Обеспечивает более читаемый код в случаях, когда существует несколько конструкторов, каждый по разной причине.
Например, если есть два конструктора Автомобиль (марка: строка, двигатель: номер) и Автомобиль (марка: строка, владелец: строка, лицензия: номер, покупка: дата) более читабельная конструкция классов будет использовать Vehicle.CreateOwnership (марка: строка, владелец: строка, лицензия: номер, покупка: дата) против Vehicle.Create (марка: строка, двигатель: номер)
  • Позволяет классу откладывать создание экземпляров до подклассов и предотвращать непосредственное создание экземпляров объекта родительского типа.
Например, можно предотвратить создание экземпляра Vehicle напрямую, поскольку у него нет конструктора, и можно создать только подклассы, такие как ElectricPlane или OldCar, путем вызова (статического) фабричного метода Vehicle в конструкторе или инициализаторе подкласса.

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

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

Определение

«Определите интерфейс для создания объекта, но позвольте подклассам решать, какой класс создать. Метод Factory позволяет классу отложить создание экземпляра, которое он использует, до подклассов». (Банда четырех )

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

Шаблон фабричного метода основан на наследовании, поскольку создание объекта делегируется подклассам, которые реализуют фабричный метод для создания объектов.[3]

Структура

Диаграмма классов UML

Пример диаграммы классов UML для шаблона проектирования Factory Method. [4]

В приведенном выше UML диаграмма классов, то Создатель класс, который требует Товар объект не создает экземпляр Товар1 class напрямую. Создатель относится к отдельному factoryMethod () для создания объекта продукта, который делает Создатель независимо от того, какой конкретный класс создается. Создатель может переопределить, какой класс создавать. В этом примере Создатель1 подкласс реализует абстрактный factoryMethod () путем создания экземпляра Товар1 класс.

пример

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

Структура

Новый WikiFactoryMethod.png

Номер базовый класс для конечного продукта (MagicRoom или Обычный номер). Лабиринт объявляет абстрактный фабричный метод для производства такого базового продукта. MagicRoom и Обычный номер являются подклассами базового продукта, реализующими конечный продукт. MagicMazeGame и Обычный ЛабиринтИгры являются подклассами Лабиринт внедрение заводского метода производства конечной продукции. Таким образом, фабричные методы разделяют вызывающих (Лабиринт) от реализации конкретных классов. Это делает «нового» оператора избыточным, позволяет придерживаться Принцип открытия / закрытия и делает конечный продукт более гибким в случае изменения.

Примеры реализации

C #

// Пустой словарь реального объектаобщественный интерфейс IPerson{    строка GetName();}общественный класс Сельский житель : IPerson{    общественный строка GetName()    {        вернуть "Деревенский человек";    }}общественный класс CityPerson : IPerson{    общественный строка GetName()    {        вернуть "Городской человек";    }}общественный перечислить PersonType{    Сельский,    Городской}/// <резюме>/// Реализация Factory - Используется для создания объектов./// общественный класс Завод{    общественный IPerson GetPerson(PersonType тип)    {        переключатель (тип)        {            кейс PersonType.Сельский:                вернуть новый Сельский житель();            кейс PersonType.Городской:                вернуть новый CityPerson();            по умолчанию:                бросить новый NotSupportedException();        }    }}

В приведенном выше коде вы можете увидеть создание одного интерфейса с именем IPerson и две реализации под названием Сельский житель и CityPerson. На основании типа, переданного в Завод объект, мы возвращаем исходный конкретный объект в качестве интерфейса IPerson.

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

общественный интерфейс IPпродукт{    строка GetName();    строка Установить цену(двойной цена);}общественный класс Телефон : IPпродукт{    частный двойной _цена;    общественный строка GetName()    {        вернуть «Apple TouchPad»;    }    общественный строка Установить цену(двойной цена)    {        _цена = цена;        вернуть "успех";    }}/ * Почти то же самое, что и Factory, только дополнительная возможность что-то сделать с созданным методом * /общественный Абстрактные класс ПродуктАннотацияФабрика{    защищенный Абстрактные IPпродукт MakeProduct();    общественный IPпродукт GetObject() // Реализация фабричного метода.    {        вернуть этот.MakeProduct();    }}общественный класс ТелефонБетонЗавод : ПродуктАннотацияФабрика{    защищенный отменять IPпродукт MakeProduct()    {        IPпродукт товар = новый Телефон();        // Сделайте что-нибудь с объектом после того, как получите объект.        товар.Установить цену(20.30);        вернуть товар;    }}

Вы можете видеть, что мы использовали MakeProduct в бетонном заводе. В результате вы легко можете позвонить MakeProduct () от него, чтобы получить IPпродукт. Вы также можете написать свою собственную логику после получения объекта в конкретном фабричном методе. GetObject делается абстрактным в интерфейсе Factory.

Ява

Эта Ява пример похож на тот, что в книге Шаблоны проектирования.

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

общественный Абстрактные класс Номер {    Абстрактные пустота соединять(Номер номер);}общественный класс MagicRoom расширяет Номер {    общественный пустота соединять(Номер номер) {}}общественный класс Обычный номер расширяет Номер {    общественный пустота соединять(Номер номер) {}}общественный Абстрактные класс Лабиринт {     частный окончательный Список<Номер> номера = новый ArrayList<>();     общественный Лабиринт() {          Номер комната1 = освободить место();          Номер комната2 = освободить место();          комната1.соединять(комната2);          номера.Добавить(комната1);          номера.Добавить(комната2);     }     Абстрактные защищенный Номер освободить место();}

В приведенном выше фрагменте Лабиринт конструктор шаблонный метод это дает некоторую общую логику. Это относится к освободить место factory, который инкапсулирует создание комнат, так что другие комнаты могут использоваться в подклассе. Чтобы реализовать другой режим игры, в котором есть волшебные комнаты, достаточно переопределить освободить место метод:

общественный класс MagicMazeGame расширяет Лабиринт {    @Override    защищенный Номер освободить место() {        вернуть новый MagicRoom();    }}общественный класс Обычный ЛабиринтИгры расширяет Лабиринт {    @Override    защищенный Номер освободить место() {        вернуть новый Обычный номер();    }}Лабиринт обычная игра = новый Обычный ЛабиринтИгры();Лабиринт magicGame = новый MagicMazeGame();

PHP

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

/ * Заводские и автомобильные интерфейсы * /интерфейс Автомобильный завод{    общественный функция makeCar(): Машина;}интерфейс Машина{    общественный функция getType(): строка;}/ * Конкретные реализации завода и автомобиля * /класс СеданЗавод орудия Автомобильный завод{    общественный функция makeCar(): Машина    {        вернуть новый Седан();    }}класс Седан орудия Машина{    общественный функция getType(): строка    {        вернуть 'Седан';    }}/ * Клиент * /$ factory = новый СеданЗавод();$ машина = $ factory->makeCar();Распечатать $ машина->getType();

Python

То же, что и в примере Java.

от abc импорт ABC, абстрактный методкласс Лабиринт(ABC):    def __в этом__(я) -> Никто:        я.номера = []        я._prepare_rooms()    def _prepare_rooms(я) -> Никто:        комната1 = я.освободить место()        комната2 = я.освободить место()        комната1.соединять(комната2)        я.номера.добавить(комната1)        я.номера.добавить(комната2)    def играть в(я) -> Никто:        Распечатать("Игра с использованием"{}"'.формат(я.номера[0]))    @abstractmethod    def освободить место(я):        поднять NotImplementedError("Вы должны реализовать это!")класс MagicMazeGame(Лабиринт):    def освободить место(я):        вернуть MagicRoom()класс Обычный ЛабиринтИгры(Лабиринт):    def освободить место(я):        вернуть Обычный номер()класс Номер(ABC):    def __в этом__(я) -> Никто:        я.connected_rooms = []    def соединять(я, номер) -> Никто:        я.connected_rooms.добавить(номер)класс MagicRoom(Номер):    def __str__(я):        вернуть «Волшебная комната»класс Обычный номер(Номер):    def __str__(я):        вернуть «Обычная комната»обычная игра = Обычный ЛабиринтИгры()обычная игра.играть в()magicGame = MagicMazeGame()magicGame.играть в()

Использует

  • В ADO.NET, IDbCommand.CreateParameter это пример использования фабричного метода для соединения параллельных иерархий классов.
  • В Qt, QMainWindow :: createPopupMenu - это фабричный метод, объявленный во фреймворке, который можно переопределить в код приложения.
  • В Ява, несколько заводов используются в javax.xml.parsers пакет. например javax.xml.parsers.DocumentBuilderFactory или javax.xml.parsers.SAXParserFactory.
  • в HTML5 ДОМ API, интерфейс Document содержит фабричный метод createElement для создания определенных элементов интерфейса HTMLElement.

Смотрите также

использованная литература

  1. ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон Уэсли. стр.107ff. ISBN  0-201-63361-2.CS1 maint: несколько имен: список авторов (ссылка на сайт)
  2. ^ «Шаблон проектирования фабричного метода - проблема, решение и применимость». w3sDesign.com. Получено 2017-08-17.
  3. ^ Фриман, Эрик; Фриман, Элизабет; Кэти, Сьерра; Берт, Бейтс (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка). 1. О'РЕЙЛИ. п. 162. ISBN  978-0-596-00712-6. Получено 2012-09-12.
  4. ^ «Шаблон проектирования фабричного метода - структура и взаимодействие». w3sDesign.com. Получено 2017-08-12.

внешние ссылки