HTML / CSSJavaScriptNode jsПаттерны проектированияПрактические

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

Суть паттерна

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

factory

Представьте, что вы создаёте программу управления грузовыми перевозками. Сперва вы рассчитываете перевозить товары только на автомобилях. Поэтому весь ваш код работает с объектами класса Грузовикs.

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

Отличные новости, правда?! Но как насчёт кода? Большая часть существующего кода жёстко привязана к классам Грузовиков. Чтобы добавить в программу классы морских Судов, понадобится перелопатить всю программу. Более того, если вы потом решите добавить в программу ещё один вид транспорта, то всю эту работу придётся повторить.

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

Решение

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

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

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

Например, классы Грузовик и Судно реализуют интерфейс Транспорт с методом доставить. Каждый из этих классов реализует метод по-своему: грузовики везут грузы по земле, а суда — по морю. Фабричный метод в классе ДорожнойЛогистики вернёт объект-грузовик, а класс МорскойЛогистики — объект-судно.

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

Пример

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

1class IceCreamFactory {
2 constructor() {
3 this.createIcecream = function(flavor) {
4 let iceCream;
5 if (flavor === 'chocolate'){
6 iceCream = new Chocolate();
7 }
8 else if (flavor === 'mint'){
9 iceCream = new Mint();
10 }
11 else if (flavor === 'strawberry'){
12 iceCream = new Strawberry();
13 }
14 return iceCream;
15 };
16 }
17}
18
19class Chocolate {
20 constructor() {
21 this.icecreamFlavor = "chocolate";
22 this.message = function() {
23 return `You chose the ${this.icecreamFlavor} flavor.`;
24 };
25 }
26}
27
28class Mint {
29 constructor() {
30 this.icecreamFlavor = "mint";
31 this.message = function() {
32 return `You chose the ${this.icecreamFlavor} flavor.`;
33 };
34 }
35}
36
37class Strawberry{
38 constructor() {
39 this.icecreamFlavor = "strawberry";
40 this.message = function() {
41 return `You chose the ${this.icecreamFlavor} flavor.`;
42 };
43 }
44}
45
46// creating objects
47const iceCreamfactory = new IceCreamFactory();
48
49const chocolate = iceCreamfactory.createIcecream('chocolate');
50const mint = iceCreamfactory.createIcecream('mint');
51const strawberry = iceCreamfactory.createIcecream('strawberry');
52
53console.log(chocolate.message());
54console.log(mint.message());
55console.log(strawberry.message());

В приведенном выше примере мы создали фабрику IceCreamFactory. В его конструкторе есть функция createIcecream, которая принимает тип параметра. В зависимости от вкуса он создает экземпляр объекта соответствующего класса. Например, если ароматизатор шоколадный, он создает экземпляр объекта класса Chocolate (строка 6). То же самое происходит с ароматом мяты или клубники (строки 9 и 12).

ice cream factory

В приведенном выше примере показано, как можно использовать один общий «фабричный метод» для создания различных объектов с похожими характеристиками.

Когда использовать?

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

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

  • Избавляет класс от привязки к конкретным классам продуктов.
  • Выделяет код производства продуктов в одно место, упрощая поддержку кода.
  • Упрощает добавление новых продуктов в программу.
  • Реализует принцип открытости/закрытости.

Недостатки

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

Более подробнее почитать о паттерне фабричный метод можно тут

Hello