抽象工厂模式

意图

抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

问题

要创建一个咖啡店,我们需要具有共同主题的对象。现代风格的咖啡店需要现代风格的椅子、现代风格的沙发和现代风格的咖啡桌,而维多利亚风格的咖啡店需要维多利亚风格的椅子、维多利亚风格的沙发和维多利亚风格的咖啡桌。咖啡店中的对象之间存在依赖关系。

假设你正在开发一款咖啡店模拟器。 你的代码中包括一些类, 用于表示:

  1. 一系列相关产品, 例如椅子Chair 、 沙发Sofa和 咖啡桌Coffee­Table
  2. 系列产品的不同变体。 例如, 你可以使用现代Modern 、 维多利亚Victorian等风格生成椅子、沙发和咖啡桌。

你需要设法单独生成每件家具对象, 这样才能确保其风格一致。 如果顾客收到的家具风格不一样, 他们可不会开心。

此外, 你也不希望在添加新产品或新风格时修改已有代码。 家具供应商对于产品目录的更新非常频繁, 你不会想在每次更新时都去修改核心代码的。

解决方案

首先, 抽象工厂模式建议为系列中的每件产品明确声明接口 (例如椅子、 沙发或咖啡桌)。 然后, 确保所有产品变体都继承这些接口。 例如, 所有风格的椅子都实现 椅子接口; 所有风格的咖啡桌都实现 咖啡桌接口, 以此类推。

接下来, 我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。 例如 create­Chair创建椅子 、 createSofa创建沙发和 create­Coffee­Table创建咖啡桌 。 这些方法必须返回抽象产品类型, 即我们之前抽取的那些接口: 椅子沙发咖啡桌等等。

那么该如何处理产品变体呢? 对于系列产品的每个变体, 我们都将基于 抽象工厂接口创建不同的工厂类。 每个工厂类都只能返回特定类别的产品, 例如, 现代家具工厂Modern­Furniture­Factory只能创建 现代椅子Modern­Chair 、 现代沙发Modern­Sofa和 现代咖啡桌Modern­Coffee­Table对象。

抽象工厂模式结构

程序示例

首先,我们为咖啡店中的对象提供了一些接口和实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public interface Chair {
String getDescription();
}

public interface Sofa {
String getDescription();
}

public interface CoffeeTable {
String getDescription();
}

// Modern implementations ->
public class ModernChair implements Chair {
static final String DESCRIPTION = "这是现代风格的椅子!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ModernSofa implements Sofa {
static final String DESCRIPTION = "这是现代风格的沙发!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ModernCoffeeTable implements CoffeeTable {
static final String DESCRIPTION = "这是现代风格的咖啡桌!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

// Victorian implementations similarly -> ...

然后我们有了家具工厂的抽象和实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
CoffeeTable createCoffeeTable();
}

public class ModernFurnitureFactory implements KingdomFactory {
public Chair createChair() {
return new ModernChair();
}
public Sofa createSofa() {
return new ModernSofa();
}
public CoffeeTable createCoffeeTable() {
return new ModernCoffeeTable();
}
}

public class VictorianFurnitureFactory implements KingdomFactory {
public Chair createChair() {
return new VictorianChair();
}
public Sofa createSofa() {
return new VictorianSofa();
}
public CoffeeTable createCoffeeTable() {
return new VictorianCoffeeTable();
}
}

现在,我们可以为不同的家具工厂设计工厂。 在此示例中,我们创建了FactoryMaker,负责返回ModernFurnitureFactoryVictorianFurnitureFactory的实例。 客户可以使用FactoryMaker来创建所需的具体工厂,该工厂随后将生产不同的具体对象(椅子,沙发,咖啡桌)。 在此示例中,我们还使用了一个枚举来参数化客户要求的家具工厂类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Getter
@Setter
public class CoffeeStore {

private Chair chair;

private Sofa sofa;

private CoffeeTable coffeeTable;

public static class FactoryMaker {

public enum CoffeeStoreStyle {
Victorian, Modern
}

public static FurnitureFactory makeFactory(CoffeeStoreStyle style) {
FurnitureFactory factory;
switch (style) {
case Victorian:
factory = new VictorianFurnitureFactory();
break;
case Modern:
factory = new ModernFurnitureFactory();
break;
default: throw new IllegalArgumentException("FurnitureType not supported.");
};
return factory;
}

}

}

现在我们有了抽象工厂,使我们可以制作相关对象的系列,即现代风格工厂创建了现代风格椅子,现代风格沙发和现代风格咖啡桌等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) {

CoffeeStore coffeeStore = new CoffeeStore();
System.out.println("Victorian Style");
FurnitureFactory victorianFactory = CoffeeStore.FactoryMaker.makeFactory(CoffeeStore.FactoryMaker.CoffeeStoreStyle.Victorian);
coffeeStore.setChair(victorianFactory.createChair());
coffeeStore.setSofa(victorianFactory.createSofa());
coffeeStore.setCoffeeTable(victorianFactory.createCoffeeTable());
System.out.println(coffeeStore.getChair().getDescription());
System.out.println(coffeeStore.getSofa().getDescription());
System.out.println(coffeeStore.getCoffeeTable().getDescription());

System.out.println("Modern Style");
FurnitureFactory ModernFactory = CoffeeStore.FactoryMaker.makeFactory(CoffeeStore.FactoryMaker.CoffeeStoreStyle.Modern);
coffeeStore.setChair(ModernFactory.createChair());
coffeeStore.setSofa(ModernFactory.createSofa());
coffeeStore.setCoffeeTable(ModernFactory.createCoffeeTable());
System.out.println(coffeeStore.getChair().getDescription());
System.out.println(coffeeStore.getSofa().getDescription());
System.out.println(coffeeStore.getCoffeeTable().getDescription());

}

程序输出:

1
2
3
4
5
6
7
8
Victorian Style
这是维多利亚风格的椅子!
这是维多利亚风格的沙发!
这是维多利亚风格的咖啡桌!
Modern Style
这是现代风格的椅子!
这是现代风格的沙发!
这是现代风格的咖啡桌!

适用性

在以下情况下使用抽象工厂模式

  • 该系统应独立于其产品的创建,组成和表示方式
  • 系统应配置有多个产品系列之一
  • 相关产品对象系列旨在一起使用,你需要强制执行此约束
  • 你想提供产品的类库,并且只想暴露它们的接口,而不是它们的实现。
  • 从概念上讲,依赖项的生存期比使用者的生存期短。
  • 你需要一个运行时值来构建特定的依赖关系
  • 你想决定在运行时从系列中调用哪种产品。
  • 你需要提供一个或更多仅在运行时才知道的参数,然后才能解决依赖关系。
  • 当你需要产品之间的一致性时
  • 在向程序添加新产品或产品系列时,您不想更改现有代码。

示例场景

  • 在运行时在FileSystemAcmeServiceDatabaseAcmeServiceNetworkAcmeService中选择并调用一个
  • 单元测试用例的编写变得更加容易
  • 适用于不同操作系统的UI工具

影响

  • Java中的依赖注入会隐藏服务类的依赖关系,这些依赖关系可能导致运行时错误,而这些错误在编译时会被捕获。

  • 虽然在创建预定义对象时模式很好,但是添加新对象可能会很困难。

  • 由于引入了许多新的接口和类,因此代码变得比应有的复杂。

已知使用

  • javax.xml.parsers.DocumentBuilderFactoryopen in new window
  • javax.xml.transform.TransformerFactoryopen in new window
  • javax.xml.xpath.XPathFactoryopen in new window