Зачем нужен фабричный метод
Фабричный метод (англ. Factory Method) — порождающий паттерн (шаблон) проектирования, предоставляющий подклассаминтерфейс для создания экземпляров некоторого класса. В момент создания наследники могут определить, какойкласс создавать. Иными словами, Фабрика делегирует создание объектов наследникам родительского класса. Этопозволяет использовать в коде программы не специфические классы, а манипулировать абстрактными объектами наболее высоком уровне. Также известен под названием виртуальный конструктор (англ. Virtual Constructor).
Цель
Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой классинстанциировать. Фабричный метод позволяет классу делегировать создание подклассов. Используется, когда:
- классу заранее неизвестно, объекты каких подклассов ему нужно создавать.
- класс спроектирован так, чтобы объекты, которые он создаёт, специфицировались подклассами.
- класс делегирует свои обязанности одному из нескольких вспомогательных подклассов, и планируетсялокализовать знание о том, какой класс принимает эти обязанности на себя.
Плюсы
- позволяет сделать код создания объектов более универсальным, не привязываясь к конкретным классам(ConcreteProduct), а оперируя лишь общим интерфейсом (Product);
- позволяет установить связь между параллельными иерархиями классов.
Минусы
- необходимость создавать наследника Creator для каждого нового типа продукта (ConcreteProduct).
- Product — продукт
- определяет интерфейс объектов, создаваемых абстрактным методом;
- реализует интерфейс Product;
- объявляет фабричный метод, который возвращает объект типа Product. Может также содержатьреализацию этого метода «по умолчанию»;
- может вызывать фабричный метод для создания объекта типа Product;
- переопределяет фабричный метод таким образом, чтобы он создавал и возвращал объект классаConcreteProduct.
1: 2:
3: abstract class Product
4: 5: >
6:
7: class ProductA extends Product
8: 9: >
10:
11: class ProductB extends Product
12: 13: >
14:
15: abstract class FactoryAbstract
16: 17: public function create ( $type )
18: 19: switch ( $type ) 20: case ‘A’ :
21: return new ProductA ();
22: case ‘B’ :
23: default:
24: return new ProductB ();
25: >
26: >
27: >
28:
29: class Factory extends FactoryAbstract
30: 31: >
32:
33: $factory = new Factory ();
34: $productA = $factory -> create ( ‘A’ );
?phpПаттерн фабричный метод
Скорее всего до этого момента ты уже сталкивался с паттернами проектирования. Например, с одиночкой (singleton).
Давай вспомним, что такое паттерны, зачем они нужны, что такое порождающие паттерны (к которым и относится одиночка), и изучим новый паттерн — фабричный метод.
Шаблон проектирования или паттерн (design pattern) в разработке программного обеспечения — это повторяемая архитектурная конструкция, которая представляет собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.
Обычно шаблон не является законченным образцом, который может быть прямо преобразован в код, это лишь пример решения задачи, который можно использовать в различных ситуациях.
Порождающие шаблоны (creational patterns) — шаблоны проектирования, которые имеют дело с процессом создания объектов. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов.
Фабричный метод (factory method) — это порождающий паттерн проектирования, который определяет общий интерфейс для создания объектов в родительском классе, предоставляя возможность создания этих самых объектов своим наследникам. В момент создания наследники могут определить, какой класс создавать.
Какую проблему решает паттерн?
Представь, что ты решил создать программу доставки. Изначально ты будешь нанимать курьеров с автомобилями и в программе в качестве средства доставки использовать объект Автомобиль . Курьеры развозят посылки из пункта А в пункт Б, В и так далее. Всё просто.
Программа набирает популярность, твой бизнес растет, ты хочешь расширяться, выходя на новые рынки. Так, например, можно дополнительно начать доставлять еду и заниматься грузовыми перевозками. Тогда еду могут доставлять и пешие курьеры, и на самокатах, и на велосипедах, а под грузовые нужды нужны грузовые автомобили.
Теперь тебе важно знать, когда, кому, что и сколько конкретно будет доставлено, учитывая, сколько каждый курьер может перевозить или переносить. У новых видов транспортных средств разная скорость и вместимость. Тогда ты обнаружишь, что большая часть сущностей в программе сильно связаны с объектом Автомобиль , и чтобы заставить твою программу работать с другими способами доставки, тебе придется переписывать имеющуюся кодовую базу и так повторять каждый раз для каждого нового транспорта.
В итоге получается ужасающий код, наполненный условными операторами, которые выполняют то или иное действие в зависимости от транспорта.
Решение проблемы
Паттерн фабричный метод предлагает создавать объекты не напрямую, используя оператор new , а через вызов особого фабричного метода. Подклассы класса, который содержит фабричный метод, могут изменять создаваемые объекты конкретных создаваемых транспортных средств. На первый взгляд это может показаться бессмысленным: мы просто переместили вызов конструктора из одного конца программы в другой. Но теперь ты сможешь переопределять фабричный метод в подклассе, чтобы изменить тип создаваемого транспорта.
Посмотрим на диаграмму классов такого подхода:
Чтобы эта система заработала, все возвращаемые объекты должны иметь общий интерфейс. Подклассы смогут производить объекты различных классов, следующих одному и тому же интерфейсу.
Например, классы Грузовик и Автомобиль реализуют интерфейс Курьерский Транспорт с методом доставить . Каждый из этих классов реализует метод по-своему: грузовики доставляют грузы, а автомобили — еду, посылки и так далее. Фабричный метод в классе Создатель грузовиков вернёт объект-грузовик, а класс Создатель автомобилей — объект-автомобиль.
Для клиента фабричного метода нет разницы между этими объектами, так как он будет трактовать их как некий абстрактный Курьерский Транспорт . Для него будет важно, чтобы объект имел метод доставить, а как конкретно он работает — не важно.
Реализация на Java:
public interface CourierTransport < void deliver(); >public class Car implements CourierTransport < @Override public void deliver() < System.out.println("The parcel is delivered by car "); >> public class Truck implements CourierTransport < @Override public void deliver() < System.out.println("Cargo is delivered by truck"); >> public abstract class CourierTransportCreator < public abstract CourierTransport createTransport(); >public class CarCreator extends CourierTransportCreator < @Override public CourierTransport createTransport() < return new Car(); >> public class TruckCreator extends CourierTransportCreator < @Override public CourierTransport createTransport() < return new Truck(); >> public class Deliver < private String address; private CourierTransport courierTransport; public Deliver() < >public Deliver(String address, CourierTransport courierTransport) < this.address = address; this.courierTransport = courierTransport; >public CourierTransport getCourierTransport() < return courierTransport; >public void setCourierTransport(CourierTransport courierTransport) < this.courierTransport = courierTransport; >public String getAddress() < return address; >public void setAddress(String address) < this.address = address; >> public static void main(String[] args) < //принимаем новый вид заказа с базы (псевдокод) String type = database.getTypeOfDeliver(); Deliver deliver = new Deliver(); //заполняем транспорт в доставку deliver.setCourierTransport(getCourierTransportByType(type)); //доставляем deliver.getCourierTransport().deliver(); >public static CourierTransport getCourierTransportByType(String type) < switch (type) < case "CarDeliver": return new CarCreator().createTransport(); case "TruckDeliver": return new TruckCreator().createTransport(); default: throw new RuntimeException(); >>
Если мы захотим создать новый объект доставки, то программа автоматически от её вида создаст нам объект транспорта.
Когда применять паттерн?
1. Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать твой код.
Фабричный метод отделяет код производства транспорта от остального кода, который этот транспорт использует. Благодаря этому код создания объектов можно расширять, не трогая основной.
Так, чтобы добавить поддержку нового транспорта, тебе нужно создать новый подкласс и определить в нём фабричный метод, возвращая оттуда экземпляр нового транспорта.
2. Когда ты хочешь экономить системные ресурсы, повторно используя уже созданные объекты вместо порождения новых.
Такая проблема обычно возникает при работе с тяжёлыми ресурсоемкими объектами, такими, как подключение к базе данных, файловой системе и т. д.
Представь, сколько действий тебе нужно совершить, чтобы повторно использовать существующие объекты:
- Сначала тебе следует создать общее хранилище, чтобы хранить в нем все создаваемые объекты.
- При запросе нового объекта нужно будет заглянуть в хранилище и проверить, есть ли там неиспользуемый объект.
- Вернуть объект клиентскому коду.
- Но если свободных объектов нет — создай новый, добавив его в хранилище.
Весь этот код нужно куда-то поместить, чтобы не засорять клиентский код. Самым удобным местом был бы конструктор объекта, ведь все эти проверки нужны только при создании объектов. Но, увы, конструктор всегда создаёт новые объекты, он не может вернуть существующий экземпляр.
Значит, нужен другой метод, который бы отдавал как существующие, так и новые объекты. Им и станет фабричный метод.
3. Когда ты хочешь дать возможность пользователям расширять части твоего фреймворка или библиотеки.
Пользователи могут расширять классы твоего фреймворка через наследование. Но как сделать так, чтобы фреймворк создавал объекты из этих новых классов, а не из стандартных?
Решением будет дать пользователям возможность расширять не только желаемые компоненты, но и классы, которые создают эти компоненты. А для этого создающие классы должны иметь конкретные создающие методы, которые можно определить.
Преимущества
- Избавляет класс от привязки к конкретным классам транспорта.
- Выделяет код создания транспорта в одно место, упрощая поддержку кода.
- Упрощает добавление новых видов транспорта в программу.
- Реализует принцип открытости/закрытости.
Недостатки
Может привести к созданию больших параллельных иерархий классов, так как для каждого класса продукта надо создать свой подкласс создателя.
Подведем итог
Ты познакомился с паттерном фабричный метод и увидел его возможную реализацию. Этот паттерн достаточно часто используется в различных библиотеках, которые в свою очередь предоставляют объекты для создания объектов.
Используй паттерн фабричный метод в случае, когда хочешь без проблем внедрять в свою программу новые объекты-подклассы на основе уже имеющихся для взаимодействия с основной бизнес-логикой, чтобы не сильно раздувать код из-за различного контекста.
Зачем нужен фабричный метод
♂️ Чем в конечном итоге SimpleItalianCoffeeFactory и SimpleAmericanCoffeeFactory отличаются от ItalianCoffeeShop и AmericanCoffeeShop? «Во-первых, обилие фабрик. Это что, каждый раз теперь под новую точку свою фабрику создавать и вдобавок следить за тем, чтобы при создании кофейни в конструктор передавалась нужная фабрика? » Хорошо! Внезапно я захотел открыть индусскую кофейню. Но вот проблема: мне придется создать очередной CoffeShop. В чем преимущество? «Во-вторых, это все еще простая фабрика. Просто немного модернизированная. Мы тут все-таки новый паттерн изучаем. В-третьих, а что, нельзя что ли по-другому? Вот было бы классно, если бы мы могли локализовать все вопросы по приготовлению кофе внутри класса CoffeeShop, связав процессы по созданию кофе и обслуживанию заказа, но при этом сохранив достаточную гибкость, чтобы делать кофе в различных стилях. » Где гибкость? Все то же самое, с немного изменённой структурой? Что так — что так придется одинаковое количество кода писать/копипастить. P.S. Ни фабрика ни фабричный метод не подходят, когда предполагается, что количество реализаций однотипных объектов будет увеличиваться. Автор статьи выбрал очень неудачный пример для данного паттерна.
Maks Panteleev Уровень 41
26 июля 2021То ли пример гавно, то ли паттерн, самый просто вариант — добавить в енам еще 4 вида кофе и 4 кейс блока и не городить гавна. Если хотим сохранить абстракцию — то достаточно было создать два енама — итальянский и американский, абстрактный класс, две его реализации и внизу уже ниче не трогать
On Girame Уровень 20
9 июня 2021Если у нас появляется американская кофейня, итальянская кофейня, то нам уже нужна «Абстрактная фабрика»
Зачем нужен фабричный метод?
Какая выгода нам от создания объекта через Creator , если точно так же можно его создать через new ? Когда нужно использовать именно фабричный метод, и какое преимущество нам это может дать?
Отслеживать
задан 17 мая 2016 в 9:25
3,486 2 2 золотых знака 27 27 серебряных знаков 51 51 бронзовый знак2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Выгод может быть много. Некоторые:
-
Вы можете дать имя вашему конструктору. Например:
Point PointCreator.FromPolar(double r, double phi)
Point PointCreator.FromPolar(double r, double phi) < . >Point PointCreator.FromCartesian(double x, double y)
или например
DateTime DateTime.FromSeconds(double seconds) < . >DateTime DateTime.FromMilliseconds(double ms)
WebRequest InitWebRequest(string uri)
async SerialDevice Create(PortInfo portInfo)
Отслеживать
ответ дан 17 мая 2016 в 9:49
207k 29 29 золотых знаков 294 294 серебряных знака 528 528 бронзовых знаков
Действительно, выгод много. Спасибо за ответ!
17 мая 2016 в 10:09
@Lightness: Пожалуйста!
17 мая 2016 в 10:55Проблема в самом операторе new. Используя его, вы каждый раз создаёте экземпляр конкретного типа. А фабричный метод позволяет абстрагироваться от конкретных типов и передать ответственность за создание экземпляров реализации этого паттерна. На выходе вы получаете некий абстрактный тип Product, который обладает теми или иными интересующими вас свойствами, но конкретная реализация которого вам не важна.
Отслеживать
ответ дан 17 мая 2016 в 9:37
DreamChild DreamChild
36.3k 3 3 золотых знака 45 45 серебряных знаков 85 85 бронзовых знаков- c#
- шаблоны-проектирования
-
Важное на Мете
Связанные
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.4.30.8420