你好!今天我们继续学习设计模式,聊聊工厂方法(FactoryMethod)。 您将了解它是什么以及该模板适合什么任务。我们将在实践中研究这种设计模式并探索其结构。为了让您清楚地了解上述所有内容,您需要了解以下主题:
- Java 中的继承。
- Java 中的抽象方法和类。
工厂方法解决什么问题?
在所有工厂设计模式中,都有两组参与者——创建者(工厂本身)和产品(工厂创建的对象)。想象一下这种情况:我们有一家工厂生产 AutoRush 品牌的汽车。她可以创建具有不同车身类型的汽车模型:- 轿车
- 旅行车
- 轿跑车
- AutoRush 轿车
- AutoRush 旅行车
- 轿跑车 AutoRush
- OneAuto轿车
- OneAuto 旅行车
- 轿跑车OneAuto
关于工厂模板的一些知识
让我提醒您:我们与您一起建立了一家小型虚拟咖啡店。在其中,我们学习了如何使用简单的工厂制作不同类型的咖啡。今天我们将完善这个例子。让我们记住我们的咖啡店和一个简单的工厂是什么样子的。我们上了一堂咖啡课:public class Coffee {
public void grindCoffee(){
// перемалываем кофе
}
public void makeCoffee(){
// делаем кофе
}
public void pourIntoCup(){
// наливаем в чашку
}
}
还有他的几位继承人 - 我们工厂可以生产的特定类型的咖啡:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
为了方便接受订单,我们推出了转账:
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("Вот ваш кофе! Спасибо, приходите еще!");
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("Вот ваш кофе! Спасибо, приходите еще!");
return coffee;
}
我们有什么选择?我们已经知道如何编写工厂了,对吧?立即想到的最简单的事情就是编写两个类似的工厂,然后在构造函数中将所需的实现传递给我们的咖啡店。那么咖啡店的等级就不会改变。首先,我们需要创建一个新的工厂类,继承我们的简单工厂并覆盖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。让我们看看从不同咖啡店订购咖啡的代码是什么样的。例如,意大利和美国风格的卡布奇诺:
public class Main {
public static void main(String[] args) {
/*
Закажем капучино в итальянском стиле:
1. Создадим фабрику для приготовления итальянского кофе
2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
3. Закажем наш кофе
*/
SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
/*
Закажем капучино в американском стиле
1. Создадим фабрику для приготовления американского кофе
2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
3. Закажем наш кофе
*/
SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
}
}
我们创建了两个不同的咖啡店,并将每个咖啡店转移到所需的工厂。一方面,我们实现了目标,但另一方面……有些东西正在抓挠企业家无法抑制的灵魂……让我们找出问题所在。首先,工厂丰富。是否可以每次为一个新点创建自己的工厂,并且确保在创建咖啡店时将所需的工厂传递给构造函数?其次,它仍然是一个简单的工厂。只是现代化了一点。我们仍在研究一种新模式。第三,难道就不能采取不同的做法吗?CoffeeShop
如果我们能够在课堂上本地化有关煮咖啡的所有问题,将制作咖啡和服务订单的过程联系起来,但同时保持足够的灵活性来制作不同风格的咖啡,那就太酷了。答案是肯定的,可以。这称为工厂方法设计模式。
从简单工厂到工厂方法
为了尽可能有效地解决问题,我们:- 让我们将方法返回
createCoffee(CoffeeType type)
给类CoffeeShop
。 - 让我们把这个方法抽象化。
- 类本身
CoffeeShop
将变得抽象。 - 班级
CoffeeShop
将有继承人。
CoffeeShop
实施一种方法。createCoffee(CoffeeType type)
所以,按顺序。步骤 1. 让我们将类Coffee
抽象化。我们现在有两个不同产品系列。意大利和美国的咖啡饮料仍然有一个共同的祖先:Coffee
. 将其抽象化是正确的:
public abstract class Coffee {
public void makeCoffee(){
// делаем кофе
}
public void pourIntoCup(){
// наливаем в чашку
}
}
步骤 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("Вот ваш кофе! Спасибо, приходите еще!");
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. 让我们看看点一杯美式和意式拿铁咖啡会是什么样子:
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);
}
}
恭喜。我们刚刚在我们的咖啡厅实现了工厂方法设计模式。
工厂方法如何工作
现在让我们仔细看看我们得到了什么。下图显示了生成的类。绿色块是创建者类,蓝色块是产品类。 可以得出什么结论?- 所有产品都是抽象类的实现
Coffee
。 - 所有创建者都是抽象类的实现
CoffeeShop
。 - 我们观察到两个并行的类层次结构:
- 产品的层次结构。我们看到意大利后裔和美国后裔
- 创作者的等级制度。我们看到意大利后裔和美国后裔
- 超类
CoffeeShop
没有关于Coffee
将创建哪个特定产品实现 ( ) 的信息。 - 超类
CoffeeShop
将特定产品的创建委托给其后代。 - 每个后代类根据其具体情况
CoffeeShop
实现一个工厂方法。createCoffee()
换句话说,在创建者类的实现中,根据创建者类的具体情况决定准备特定的产品。
工厂方法结构
上图展示了工厂方法模式的总体结构。这里还有什么重要的呢?- Creator 类包含与产品交互的所有方法的实现(工厂方法除外)。
- 抽象方法
factoryMethod()
必须由该类的所有后代实现Creator
。 - 该类实现了直接生产产品的
ConcreteCreator
方法。factoryMethod()
- 该类负责创建特定的产品。这是唯一包含有关创建这些产品的信息的课程。
- 所有产品都必须实现一个公共接口——成为公共产品类的后代。这是必要的,以便使用产品的类可以在抽象级别而不是具体实现级别对其进行操作。
GO TO FULL VERSION