здрасти Днес ще продължим да изучаваме моделите на проектиране и ще обсъдим модела на фабричния метод.
Ще разберете Howво представлява и за Howви задачи е подходящ този модел. Ще разгледаме този дизайн модел на практика и ще проучим неговата структура. За да сте сигурни, че всичко е ясно, трябва да разберете следните теми:
Какви изводи можем да направим?
Диаграмата по-горе показва общата структура на модела на фабричния метод. Какво друго е важно тук?

- Наследяване в Java.
- Абстрактни методи и класове в Java
Какъв проблем решава фабричният метод?
Всички модели на фабрично проектиране имат два типа участници: създатели (самите фабрики) и продукти (обектите, създадени от фабриките). Представете си следната ситуация: имаме фабрика, която произвежда автомобor с марката CodeGym. Той знае How да създава модели на автомобor с различни видове каросерии:- седани
- комбита
- купета
- CodeGym седани
- CodeGym комбита
- CodeGym купета
- OneAuto седани
- OneAuto комбита
- OneAuto купета
Малко за фабричния модел
Нека ви напомня, че преди това изградихме малко виртуално кафене. С помощта на една проста фабрика се научихме How да създаваме различни видове кафе. Днес ще преработим този пример. Нека си припомним How изглеждаше нашето кафене с неговата проста фабрика. Имахме час по кафе:
public class Coffee {
public void grindCoffee(){
// Grind the coffee
}
public void makeCoffee(){
// Brew the coffee
}
public void pourIntoCup(){
// Pour into a cup
}
}
И няколко дъщерни класа, съответстващи на конкретни видове кафе, които нашата фабрика може да произвежда:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Създадохме enum, за да улесним пequalsето на поръчки:
public enum CoffeeType {
ESPRESSO,
AMERICANO,
CAFFE_LATTE,
CAPPUCCINO
}
Самата фабрика за кафе изглеждаше така:
public class SimpleCoffeeFactory {
public Coffee createCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new Americano();
break;
case ESPRESSO:
coffee = new Espresso();
break;
case CAPPUCCINO:
coffee = new Cappuccino();
break;
case CAFFE_LATTE:
coffee = new CaffeLatte();
break;
}
return coffee;
}
}
И накрая самото кафене изглеждаше така:
public class CoffeeShop {
private final SimpleCoffeeFactory coffeeFactory;
public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
this.coffeeFactory = coffeeFactory;
}
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = coffeeFactory.createCoffee(type);
coffee.grindCoffee();
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Here's your coffee! Thanks! Come again!");
return coffee;
}
}
Модернизиране на проста фабрика
Нашето кафене работи много добре. Толкова много, че обмисляме разширяване. Искаме да отворим някои нови локации. Ние сме смели и предприемчиви, така че няма да пускаме скучни кафенета. Искаме всеки магазин да има специален привкус. Съответно, като начало ще отворим две локации: една италианска и една американска. Тези промени ще засегнат не само интериорния дизайн, но и предлаганите напитки:- в италианското кафене ще използваме изключително италиански марки кафе, със специално смилане и печене.
- американското местоположение ще има по-големи порции и ще сервираме маршмелоу с всяка поръчка.
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Но сега ще имаме 8:
public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}
public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Тъй като искаме да запазим настоящия бизнес модел, искаме методът orderCoffee(CoffeeType type)
да претърпи възможно най-малко промени. Разгледайте го:
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = coffeeFactory.createCoffee(type);
coffee.grindCoffee();
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Here's your coffee! Thanks! Come again!");
return coffee;
}
Какви опции имаме? Е, ние вече знаем How да напишем фабрика, нали? Най-простото нещо, което веднага идва на ум, е да напишем две подобни фабрики и след това да предадем желаната реализация на конструктора на нашето кафене. По този начин класът на кафенето няма да се промени. Първо, трябва да създадем нов фабричен клас, да го накараме да наследи нашата проста фабрика и след това да заменим createCoffee(CoffeeType type)
метода. Нека напишем фабрики за създаване на кафе в италиански стил и кафе в американски стил:
public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {
@Override
public Coffee createCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new ItalianStyleAmericano();
break;
case ESPRESSO:
coffee = new ItalianStyleEspresso();
break;
case CAPPUCCINO:
coffee = new ItalianStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new ItalianStyleCaffeLatte();
break;
}
return coffee;
}
}
public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{
@Override
public Coffee createCoffee (CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new AmericanStyleAmericano();
break;
case ESPRESSO:
coffee = new AmericanStyleEspresso();
break;
case CAPPUCCINO:
coffee = new AmericanStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new AmericanStyleCaffeLatte();
break;
}
return coffee;
}
}
Сега можем да предадем желаната фабрична реализация на CoffeeShop. Нека да видим How ще изглежда codeът за поръчка на кафе от различни кафенета. Например капучино в италиански и американски стил:
public class Main {
public static void main(String[] args) {
/*
Order an Italian-style cappuccino:
1. Create a factory for making Italian coffee
2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
3. Order our coffee
*/
SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
/*
Order an American-style cappuccino
1. Create a factory for making American coffee
2. Create a new coffee shop, passing the American coffee factory to it through the constructor
3. Order our coffee
*/
SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
}
}
Създадохме две различни кафенета, като предадохме желаната фабрика на всяка. От една страна, постигнахме целта си, но от друга страна... Това няHow си не им харесва на предприемачите... Нека да разберем Howво не е наред. Първо, изобorето от фабрики. Какво? Сега за всяко ново местоположение трябва да създадем собствена фабрика и в допълнение към това да се уверим, че съответната фабрика е предадена на конструктора, когато създаваме кафене? Второ, това все още е проста фабрика. Само леко модернизиран. Но ние сме тук, за да научим нов модел. Трето, не е ли възможен различен подход? Би било чудесно, ако можем да поставим всички въпроси, свързани с приготвянето на кафе, вCoffeeShop
клас чрез свързване на процесите на създаване на кафе и обслужване на поръчки, като същевременно поддържа достатъчна гъвкавост за приготвяне на различни стилове кафе. Отговорът е да, можем. Това се нарича шаблон за проектиране на фабричен метод.
От проста фабрика до фабричен метод
За да разрешите задачата възможно най-ефективно:- Връщаме
createCoffee(CoffeeType type)
метода вCoffeeShop
класа. - Ще направим този метод абстрактен.
- Самият клас
CoffeeShop
ще стане абстрактен. - Класът
CoffeeShop
ще има детски часове.
CoffeeShop
класа, който прилага createCoffee(CoffeeType type)
метода в съответствие с най-добрите традиции на италианските баристи. Сега, стъпка по стъпка. Стъпка 1. Направете Coffee
класа абстрактен. Имаме цели две семейства от различни продукти. Все пак италианското и американското кафе имат общ прародител - класа Coffee
. Би било правилно да го направим абстрактно:
public abstract class Coffee {
public void makeCoffee(){
// Brew the coffee
}
public void pourIntoCup(){
// Pour into a cup
}
}
Стъпка 2. Направете CoffeeShop
абстракт, с абстрактен createCoffee(CoffeeType type)
метод
public abstract class CoffeeShop {
public Coffee orderCoffee(CoffeeType type) {
Coffee coffee = createCoffee(type);
coffee.makeCoffee();
coffee.pourIntoCup();
System.out.println("Here's your coffee! Thanks! Come again!");
return coffee;
}
protected abstract Coffee createCoffee(CoffeeType type);
}
Стъпка 3. Създайте италианско кафене, което е наследник на абстрактното кафене. Ние внедряваме createCoffee(CoffeeType type)
метода в него, като се съобразяваме със спецификата на италианските рецепти.
public class ItalianCoffeeShop extends CoffeeShop {
@Override
public Coffee createCoffee (CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new ItalianStyleAmericano();
break;
case ESPRESSO:
coffee = new ItalianStyleEspresso();
break;
case CAPPUCCINO:
coffee = new ItalianStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new ItalianStyleCaffeLatte();
break;
}
return coffee;
}
}
Стъпка 4. Правим същото за кафенето в американски стил
public class AmericanCoffeeShop extends CoffeeShop {
@Override
public Coffee createCoffee(CoffeeType type) {
Coffee coffee = null;
switch (type) {
case AMERICANO:
coffee = new AmericanStyleAmericano();
break;
case ESPRESSO:
coffee = new AmericanStyleEspresso();
break;
case CAPPUCCINO:
coffee = new AmericanStyleCappuccino();
break;
case CAFFE_LATTE:
coffee = new AmericanStyleCaffeLatte();
break;
}
return coffee;
}
}
Стъпка 5. Вижте How ще изглеждат американското и италианското лате:
public class Main {
public static void main(String[] args) {
CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
}
}
Честито. Току-що внедрихме шаблона за проектиране на фабричен метод, използвайки нашето кафене като пример.
Принципът зад фабричните методи
Сега нека разгледаме по-подробно Howво имаме. Диаграмата по-долу показва получените класове. Зелените блокове са класове създатели, а сините блокове са класове продукти.
- Всички продукти са имплементации на абстрактния
Coffee
клас. - Всички творци са реализации на абстрактния
CoffeeShop
клас. - Виждаме две паралелни йерархии на класове:
- Йерархия на продуктите. Виждаме италиански потомци и американски потомци
- Йерархия на творците. Виждаме италиански потомци и американски потомци
- Суперкласът
CoffeeShop
няма информация кой конкретен продукт (Coffee
) ще бъде създаден. - Суперкласът
CoffeeShop
делегира създаването на конкретен продукт на своите потомци. - Всеки наследник на
CoffeeShop
класа имплементираcreateCoffee()
фабричен метод в съответствие със собствените си специфики. С други думи, реализациите на класовете производители подготвят специфични продукти въз основа на спецификата на класа производители.
Структура на фабричен метод

- Класът Creator прилага всички методи, които взаимодействат с продуктите, с изключение на фабричния метод.
- Абстрактният
factoryMethod()
метод трябва да бъде имплементиран от всички наследници наCreator
класа. - Класът
ConcreteCreator
имплементираfactoryMethod()
метода, който директно създава продукта. - Този клас отговаря за създаването на конкретни продукти. Това е единственият клас с информация за създаването на тези продукти.
- Всички продукти трябва да имат общ интерфейс, т.е. те трябва да бъдат потомци на общ продуктов клас. Това е необходимо, така че класовете, които използват продукти, да могат да работят с тях като абстракции, а не със специфични реализации.
GO TO FULL VERSION