В программировании на основе классов , то модель фабричного метода является Творением модели , которая использует фабричные методы для решения проблемы создания объектов без указания точного класса объекта , который будет создан. Это делается путем создания объектов путем вызова фабричного метода - либо указанного в интерфейсе и реализуемого дочерними классами, либо реализованного в базовом классе и необязательно переопределенного производными классами, а не путем вызова конструктора .
Обзор
Шаблон проектирования Factory Method [1] - один из шаблонов проектирования «Банда четырех», которые описывают, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать. и повторно использовать.
Шаблон проектирования Factory Method используется вместо обычного конструктора класса для соблюдения принципов программирования SOLID , отделяя построение объектов от самих объектов. Это имеет следующие преимущества и полезно, среди прочего, в следующих случаях: [2]
- Позволяет создавать классы с компонентом типа, который не был заранее определен, а только определен в «интерфейсе» или который определен как динамический тип.
- Таким образом, например, класс,
Vehicle
который имеет членMotor
интерфейсаIMotor
, но не имеет конкретного типа,Motor
определенного заранее, может быть создан, указавVehicle
конструктору использоватьElectricMotor
илиGasolineMotor
. ЗатемVehicle
код конструктора вызывает метод фабрики Motor для создания желаемогоMotor
, соответствующегоIMotor
интерфейсу.
- Позволяет создавать подклассы для родительского элемента, тип компонента которого не был заранее определен, а только определен в интерфейсе или который определен как динамический тип.
- Например, класс
Vehicle
с членом,Motor
определенным с помощью динамического типа, может иметь подклассы типа,ElectricPlane
иOldCar
каждый из них сконструирован с другим типом Motor. Это может быть выполнено путем создания подклассов с помощью фабричного метода Vehicle с указанием типа двигателя. В таких случаях конструктор может быть скрыт.
- Обеспечивает более читаемый код в случаях, когда существует несколько конструкторов, каждый по разной причине.
- Например, если есть два конструктора
Vehicle(make:string, motor:number)
иVehicle(make:string, owner:string, license:number, purchased:date)
более удобочитаемая конструкция классов будет использоватьVehicle.CreateOwnership(make:string, owner:string, license:number, purchased: date)
vsVehicle.Create(make:string, motor:number)
- Позволяет классу отложить создание экземпляра до подклассов и предотвратить прямое создание экземпляра объекта родительского типа.
- Например, можно предотвратить создание экземпляра Vehicle напрямую, поскольку у него нет конструктора, и можно создать только подклассы, такие как ElectricPlane или OldCar, путем вызова (статического) фабричного метода Vehicle в конструкторе или инициализаторе подкласса.
Создание объекта непосредственно внутри класса, который требует или использует объект, является негибким, потому что он связывает класс с конкретным объектом и делает невозможным изменение экземпляра независимо от класса. Изменение создателя экземпляра потребует изменения кода класса, которого мы не хотели бы трогать. Это упоминается как код связь и метод шаблон передачи завода в расцепления кода.
Шаблон проектирования фабричного метода используется, сначала определяя отдельную операцию, фабричный метод , для создания объекта, а затем используя этот фабричный метод , вызывая его для создания объекта. Это позволяет писать подклассы, которые решают, как создается родительский объект и какой тип объектов он содержит.
Определение
«Определите интерфейс для создания объекта, но позвольте подклассам решать, какой класс создать. Метод Factory позволяет классу отложить создание экземпляра, которое он использует, до подклассов». ( Банда четырех )
Создание объекта часто требует сложных процессов, которые нельзя включать в составной объект. Создание объекта может привести к значительному дублированию кода, может потребоваться информация не доступной для композиторского объекта, не может обеспечить достаточный уровень абстракции, или в противном случае может не быть частью слагающим объект проблем . Шаблон проектирования фабричного метода решает эти проблемы, определяя отдельный метод для создания объектов, подклассы которого затем могут переопределить, чтобы указать производный тип продукта, который будет создан.
Шаблон фабричного метода основан на наследовании, поскольку создание объекта делегируется подклассам, которые реализуют фабричный метод для создания объектов. [3]
Состав
Диаграмма классов UML
На приведенной выше диаграмме классов UML класс , Creator
которому требуется Product
объект, не создает экземпляр Product1
класса напрямую. Вместо этого Creator
ссылка на отдельный factoryMethod()
для создания объекта продукта делает Creator
независимым от того, какой конкретный класс создается. Подклассы Creator
могут переопределить, какой класс создавать. В этом примере Creator1
подкласс реализует абстрактное factoryMethod()
, создавая экземпляр Product1
класса.
Пример
В игру-лабиринт можно играть в двух режимах: один с обычными комнатами, которые связаны только с соседними комнатами, а другой с волшебными комнатами, которые позволяют перемещать игроков в случайном порядке.
Состав
Room
является базовым классом для конечного продукта ( MagicRoom
или OrdinaryRoom
). MazeGame
объявляет абстрактный фабричный метод для производства такого базового продукта. MagicRoom
и OrdinaryRoom
являются подклассами базового продукта, реализующими конечный продукт. MagicMazeGame
и OrdinaryMazeGame
являются подклассами MazeGame
реализации заводского метода производства конечных продуктов. Таким образом, фабричные методы отделяют вызывающих ( MazeGame
) от реализации конкретных классов. Это делает «нового» оператора избыточным, позволяет придерживаться принципа « открыт / закрыт» и делает конечный продукт более гибким в случае изменения.
Примеры реализации
C #
// Пустой словарь фактического публичного интерфейса объекта IPerson { string GetName (); }общественный класс Villager : IPerson { общедоступная строка GetName () { return "Village Person" ; } }общедоступный класс CityPerson : IPerson { общедоступная строка GetName () { return "Городской человек" ; } }общедоступное перечисление PersonType { Rural , Urban }/// /// Реализация Factory - Используется для создания объектов. /// общедоступный класс Factory { общедоступный IPerson GetPerson ( тип PersonType ) { переключатель ( тип ) { case PersonType . Сельский : вернуть нового жителя (); case PersonType . Городской : вернуть новый CityPerson (); по умолчанию : выбросить новое NotSupportedException (); } } }
В приведенном выше коде вы можете увидеть создание одного интерфейса с именем IPerson
и двух реализаций с именем Villager
и CityPerson
. В зависимости от типа, переданного в Factory
объект, мы возвращаем исходный конкретный объект в качестве интерфейса IPerson
.
Заводской метод - это просто дополнение к Factory
классу. Он создает объект класса через интерфейсы, но, с другой стороны, он также позволяет подклассу решать, какой класс создается.
общедоступный интерфейс IProduct { строка GetName (); bool SetPrice ( двойная цена ); }общедоступный телефон класса : IProduct { private double _price ; публичная строка GetName () { return «Apple TouchPad» ; } public bool SetPrice ( двойная цена ) { _price = price ; вернуть истину ; } }/ * Почти то же самое, что и Factory, только дополнительная возможность сделать что-то с созданным методом * / public abstract class ProductAbstractFactory { protected abstract IProduct MakeProduct (); public IProduct GetObject () // Реализация фабричного метода. { верни это . MakeProduct (); } }открытый класс PhoneConcreteFactory : ProductAbstractFactory { защищенное переопределение IProduct MakeProduct () { IProduct product = new Phone (); // Сделайте что-нибудь с объектом после того, как получите объект. продукт . SetPrice ( 20.30 ); вернуть товар ; } }
Вы можете видеть, что мы использовали MakeProduct
вcreteFactory. В результате вы можете легко позвонить MakeProduct()
с него, чтобы получить IProduct
. Вы также можете написать свою собственную логику после получения объекта в конкретном фабричном методе. GetObject делается абстрактным в интерфейсе Factory.
Ява
Этот пример Java похож на пример из книги « Шаблоны проектирования» .
MazeGame использует комнаты, но возлагает ответственность за создание комнат на свои подклассы, которые создают конкретные классы. В обычном игровом режиме можно использовать этот шаблонный метод:
общедоступный абстрактный класс Room { abstract void connect ( Room room ); }публичный класс MagicRoom расширяет комнату { public void connect ( комната комнаты ) {} }публичный класс OrdinaryRoom расширяет комнату { public void connect ( комната комнаты ) {} }общедоступный абстрактный класс MazeGame { частный окончательный список < Комната > rooms = new ArrayList <> (); public MazeGame () { Комната room1 = makeRoom (); Комната room2 = makeRoom (); комната1 . подключить ( room2 ); комнаты . добавить ( room1 ); комнаты . добавить ( комната2 ); } абстрактная защищенная комната makeRoom (); }
В приведенном выше фрагменте MazeGame
конструктор - это шаблонный метод, который реализует некоторую общую логику. Это относится к makeRoom
фабричному методу, который инкапсулирует создание комнат, так что другие комнаты могут использоваться в подклассе. Чтобы реализовать другой игровой режим, в котором есть волшебные комнаты, достаточно переопределить makeRoom
метод:
открытый класс MagicMazeGame расширяет MazeGame { @Override protected Room makeRoom () { return new MagicRoom (); } }открытый класс OrdinaryMazeGame расширяет MazeGame { @Override protected Room makeRoom () { return new OrdinaryRoom (); } }MazeGame ordinaryGame = новый OrdinaryMazeGame (); MazeGame magicGame = новая MagicMazeGame ();
PHP
Далее следует другой пример в PHP , на этот раз с использованием реализаций интерфейса, а не подклассов (однако то же самое может быть достигнуто путем создания подклассов). Важно отметить, что фабричный метод также может быть определен как общедоступный и вызываться непосредственно клиентским кодом (в отличие от приведенного выше примера Java).
/ * Заводские и автомобильные интерфейсы * /интерфейс CarFactory { общедоступная функция makeCar () : Автомобиль ; }интерфейс Автомобиль { общедоступная функция getType () : строка ; }/ * Конкретные реализации завода и автомобиля * /класс SedanFactory реализует CarFactory { общедоступную функцию makeCar () : Car { return new Sedan (); } }класс Sedan реализует Car { общедоступную функцию getType () : string { return 'Sedan' ; } }/ * Клиент * /$ factory = новый SedanFactory (); $ car = $ factory -> makeCar (); напечатать $ car -> getType ();
Python
То же, что и в примере Java.
from abc import ABC , abstractmethodclass MazeGame ( ABC ): def __init__ ( self ) -> None : self . rooms = [] self . _prepare_rooms () def _prepare_rooms ( self ) -> Нет : room1 = self . make_room () room2 = себя . make_room () комната1 . подключить ( room2 ) self . комнаты . добавить ( room1 ) self . комнаты . добавить ( room2 ) def play ( self ) -> None : print ( 'Воспроизведение с использованием " {} "' . format ( self . rooms [ 0 ])) @abstractmethod def make_room ( self ): raise NotImplementedError ( «Вы должны реализовать это!» )class MagicMazeGame ( MazeGame ): def make_room ( self ): return MagicRoom ()class OrdinaryMazeGame ( MazeGame ): def make_room ( self ): return OrdinaryRoom ()class Room ( ABC ): def __init__ ( self ) -> None : self . connected_rooms = [] def connect ( self , room ) -> None : self . connected_rooms . добавить ( комната )class MagicRoom ( Room ): def __str__ ( self ): return "Magic room"class OrdinaryRoom ( Комната ): def __str__ ( self ): return "Обычная комната"Обычная игра = Обычная игра в лабиринте () обычная игра . играть ()magicGame = MagicMazeGame () magicGame . играть ()
Использует
- В ADO.NET , IDbCommand.CreateParameter является примером использования фабричного метода для подключения параллельных иерархий классов.
- В Qt , QMainWindow :: CreatePopupMenu является фабричный метод , объявленный в рамках , которые могут быть переопределены в коде приложения .
- В Java несколько фабрик используются в пакете javax.xml.parsers . например javax.xml.parsers.DocumentBuilderFactory или javax.xml.parsers.SAXParserFactory.
- В HTML5 DOM API интерфейс Document содержит фабричный метод createElement для создания определенных элементов интерфейса HTMLElement.
Смотрите также
- Паттерны дизайна , очень влиятельная книга
- Шаблон проектирования , обзор шаблонов проектирования в целом
- Абстрактный фабричный паттерн , паттерн, часто реализуемый с использованием фабричных методов
- Паттерн Строитель , еще один творческий паттерн
- Шаблон метода шаблона , который может вызывать фабричные методы
- Идея Джошуа Блоха о статическом фабричном методе , который, по его словам, не имеет прямого эквивалента в шаблонах проектирования .
Рекомендации
- ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон Уэсли. С. 107 и далее . ISBN 0-201-63361-2.CS1 maint: несколько имен: список авторов ( ссылка )
- ^ «Шаблон проектирования фабричного метода - проблема, решение и применимость» . w3sDesign.com . Проверено 17 августа 2017 .
- ^ Фриман, Эрик; Фриман, Элизабет; Кэти, Сьерра; Берт, Бейтс (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Head First Design Patterns (мягкая обложка) . 1 . О'РЕЙЛИ. п. 162. ISBN. 978-0-596-00712-6. Проверено 12 сентября 2012 .
- ^ «Шаблон проектирования фабричного метода - структура и взаимодействие» . w3sDesign.com . Проверено 12 августа 2017 .
- Мартин Фаулер ; Кент Бек ; Джон Брант ; Уильям Опдайк ; Дон Робертс (июнь 1999 г.). Рефакторинг: улучшение дизайна существующего кода . Эддисон-Уэсли. ISBN 0-201-48567-2.
- Гамма, Эрих ; Хелм, Ричард ; Джонсон, Ральф; Влиссидес, Джон (1994). Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Эддисон-Уэсли. ISBN 0-201-63361-2.
- Кокс, Брэд Дж. (1986). Объектно-ориентированное программирование: эволюционный подход . Эддисон-Уэсли. ISBN 978-0-201-10393-9.
- Коэн, Тал; Гил, Джозеф (2007). «Лучшее строительство с фабриками» (PDF) . Журнал объектных технологий . Бертран Мейер . 6 (6): 103. doi : 10.5381 / jot.2007.6.6.a3 . Проверено 12 марта 2007 .
Внешние ссылки
- Реализация фабричного шаблона проектирования на Java
- Заводской метод в UML и LePUS3 (язык описания дизайна)
- Рассмотрим статические фабричные методы Джошуа Блоха