Привет.
Пример с сайта неудачный. Он не показывает выразительность фабричного метода в отличие от фабрики. Я изменил пример ниже.
Простая фабрика не является паттерном проектирования, это скорее идиома программирования. Простая фабрика предоставляет интерфейс для создания
одного продукта. Отличие фабричного метода в создании
набора классов, создающих экземпляры - конкретные классы расширяют класс с фабричным методом.
Один из принципов проектирования - выделите то, что изменяется и отделите от того, что остается постоянным. Все паттерны в какой-то мере обеспечивают возможность изменения некоторой части системы независимо от других частей.
Например, мы открыли в Москве и Питере магазин по продаже телефонов и в классе BeelineStore есть общие методы для всех телефонов (код на java схематичный). Samsung стал поддерживать 5G интернет, поэтому сделаем тариф для этих телефонов дороже.
spoilerabstract class IMobile {
void prepare() {
System.out.println("Подготовим документы по продаже от ООО Билайн");
}
void setInternetTariff() {
System.out.println("Установим стандартный тариф Билайна для интернета");
}
void box() {
System.out.println("Упакуем в желтый пакетик");
}
}
class BeelineSamsung extends IMobile {
void setInternetTariff() {
System.out.println("Установим отдельный тариф для 5G интернета");
}
}
class BeelineIPhone extends IMobile {}
class BeelineStore {
public IMobile orderPhone(String type) {
IMobile phone = null;
if (type.equals("samsung")) {
phone = new BeelineSamsung();
} else if (type.equals("iphone")) {
phone = new BeelineIPhone();
}
phone.prepare();
phone.setInternetTariff();
phone.box();
return phone;
}
}
// использование
// var mobileStore = new BeelineStore();
// mobileStore.orderPhone("samsung"); <- клиентский код
Однако при изменении, удалении или добавлении новых видов телефонов мы бы открывали наш класс BeelineStore и расширяли новыми конкретными типами. А если мы добавим модели телефонов, то дерево условий станет сложнее. Если одни аспекты системы изменяются, а другие остаются неизменными — пора заняться инкапсуляцией.
spoilerclass MobileFactory {
public IMobile getMobile(String type) {
if (type.equals("samsung")) {
return new BeelineSamsung();
} else if (type.equals("iphone")) {
return new BeelineIPhone();
}
return null;
}
}
class BeelineStore {
MobileFactory factory;
public BeelineStore(MobileFactory factory) {
this.factory = factory;
}
public IMobile orderPhone(String type) {
IMobile phone;
phone = factory.getMobile(type);
phone.prepare();
phone.setInternetTariff();
phone.box();
return phone;
}
}
// использование
// var mobileFactory = new MobileFactory();
// mobileStore = new BeelineStore(mobileFactory);
// mobileStore.orderPhone("samsung"); <- замечу, клиентский код не изменился
Окей - мы вынесли процесс создания телефонов - это и есть фабрика. И тут мы расширяемся, дела в нашей компании пошли хорошо и мы открываем филиалы на Урале. Тарифы на Урале будут дешевле, как стандартные, так и для 5G интернета. А также в Москве и Питере запускается программа по обмену старых телефонов на новые. И тут нам помогает фабричный метод.
spoilerabstract class BeelineStore { // класс становится абстрактным
protected abstract IMobile getMobile(String type); // возвращается метод getMobile
public IMobile orderPhone(String type) {
IMobile phone = getMobile(type);
phone.prepare();
phone.setInternetTariff();
phone.box();
return phone;
}
}
class CenterBeelineSamsung extends IMobile {
void setInternetTariff() {
System.out.println("Отдельный тариф для 5G интернета в центре России");
}
}
class CenterBeelineIPhone extends IMobile {}
class СenterBeelineStore extends BeelineStore {
public IMobile getMobile(String type) {
if (type.equals("samsung")) {
return new CenterBeelineSamsung();
} else if (type.equals("iphone")) {
return new CenterBeelineIPhone();
}
return null;
}
public void tradeIn(String phone) {
System.out.println("Вы обменяли старый телефон на новый с доплатой");
}
}
class UralBeelineSamsung extends IMobile {
void setInternetTariff() {
System.out.println("Отдельный тариф для 5G интернета на Урале");
}
}
class UralBeelineIPhone extends IMobile {
void setInternetTariff() {
System.out.println("Отдельный тариф для стандартного интернета на Урале");
}
}
class UralBeelineStore extends BeelineStore {
public IMobile getMobile(String type) {
if (type.equals("samsung")) {
return new UralBeelineSamsung();
} else if (type.equals("iphone")) {
return new UralBeelineIPhone();
}
return null;
}
}
// использование
// var centerBeelineStore = new СenterBeelineStore();
// centerBeelineStore.orderPhone("samsung");
// var uralBeelineStore = new UralBeelineStore();
// uralBeelineStore.orderPhone("samsung");
Мы смогли сохранить единые для всех магазинов стандарты оформления заказа, а также добавили региональные отличия.
То есть фабричный метод гораздо гибче. Простая Фабрика обладает узкой специализацией, а фабричный метод ведет к созданию инфраструктуры, в которой реализация выбирается субклассами.
Простая Фабрика инкапсулирует создание объектов, но она лишена гибкости фабричного метода в изменении создаваемых продуктов.