你好!今天我们继续学习设计模式,我们将讨论工厂方法模式。
您将了解它是什么以及该模式适用于哪些任务。我们将在实践中考虑这种设计模式并研究其结构。为确保一切都清楚,您需要了解以下主题:
我们可以得出什么结论?
上图显示了工厂方法模式的一般结构。这里还有什么重要的?

- Java中的继承。
- Java 中的抽象方法和类
工厂方法解决什么问题?
所有的工厂设计模式都有两类参与者:创建者(工厂本身)和产品(工厂创建的对象)。想象一下以下情况:我们有一家生产 CodeGym 品牌汽车的工厂。它知道如何创建具有各种车身类型的汽车模型:- 轿车
- 旅行车
- 双门轿车
- CodeGym 轿车
- CodeGym 旅行车
- CodeGym 跑车
- OneAuto轿车
- OneAuto旅行车
- OneAuto轿跑车
关于工厂模式的一点
让我提醒你,我们之前建立了一个小型虚拟咖啡店。在一个简单工厂的帮助下,我们学会了如何制作不同类型的咖啡。今天我们将重做这个例子。让我们回想一下我们的咖啡店的样子,里面有一个简单的工厂。我们有咖啡课:
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 {}
我们创建了一个枚举,以便于下订单:
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;
}
我们有什么选择?好吧,我们已经知道如何编写工厂了,对吧?立即想到的最简单的事情就是编写两个类似的工厂,然后将所需的实现传递给我们咖啡店的构造函数。通过这样做,咖啡店的等级不会改变。首先,我们需要新建一个工厂类,让它继承我们的简单工厂,然后重写方法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) {
/*
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);
}
}
我们创建了两个不同的咖啡店,将所需的工厂传递给每个咖啡店。一方面,我们已经实现了我们的目标,但另一方面……不知何故,这对企业家来说并不合适……让我们找出问题所在。一是工厂多。什么?现在对于每个新位置,我们应该创建自己的工厂,除此之外,确保在创建咖啡店时将相关工厂传递给构造函数?第二,它仍然是一个简单的工厂。只是稍微现代化了。但我们来这里是为了学习一种新模式。第三,是否可以采用不同的方法?如果我们能把所有与咖啡准备有关的问题都放在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;
}
}
Step 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