意图
抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
问题
要创建一个咖啡店,我们需要具有共同主题的对象。现代风格的咖啡店需要现代风格的椅子、现代风格的沙发和现代风格的咖啡桌,而维多利亚风格的咖啡店需要维多利亚风格的椅子、维多利亚风格的沙发和维多利亚风格的咖啡桌。咖啡店中的对象之间存在依赖关系。
假设你正在开发一款咖啡店模拟器。 你的代码中包括一些类, 用于表示:
- 一系列相关产品, 例如椅子
Chair
、 沙发Sofa
和 咖啡桌CoffeeTable
。
- 系列产品的不同变体。 例如, 你可以使用现代
Modern
、 维多利亚Victorian
等风格生成椅子、沙发和咖啡桌。
你需要设法单独生成每件家具对象, 这样才能确保其风格一致。 如果顾客收到的家具风格不一样, 他们可不会开心。
此外, 你也不希望在添加新产品或新风格时修改已有代码。 家具供应商对于产品目录的更新非常频繁, 你不会想在每次更新时都去修改核心代码的。
解决方案
首先, 抽象工厂模式建议为系列中的每件产品明确声明接口 (例如椅子、 沙发或咖啡桌)。 然后, 确保所有产品变体都继承这些接口。 例如, 所有风格的椅子都实现 椅子
接口; 所有风格的咖啡桌都实现 咖啡桌
接口, 以此类推。
接下来, 我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。 例如 createChair
创建椅子 、 createSofa
创建沙发和 createCoffeeTable
创建咖啡桌 。 这些方法必须返回抽象产品类型, 即我们之前抽取的那些接口: 椅子
,沙发
和 咖啡桌
等等。
那么该如何处理产品变体呢? 对于系列产品的每个变体, 我们都将基于 抽象工厂
接口创建不同的工厂类。 每个工厂类都只能返回特定类别的产品, 例如, 现代家具工厂
ModernFurnitureFactory只能创建 现代椅子
ModernChair 、 现代沙发
ModernSofa和 现代咖啡桌
ModernCoffeeTable对象。
抽象工厂模式结构
程序示例
首先,我们为咖啡店中的对象提供了一些接口和实现。
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(); }
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; } }
|
然后我们有了家具工厂的抽象和实现。
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,负责返回ModernFurnitureFactory
或VictorianFurnitureFactory
的实例。 客户可以使用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 这是现代风格的椅子! 这是现代风格的沙发! 这是现代风格的咖啡桌!
|
适用性
在以下情况下使用抽象工厂模式
- 该系统应独立于其产品的创建,组成和表示方式
- 系统应配置有多个产品系列之一
- 相关产品对象系列旨在一起使用,你需要强制执行此约束
- 你想提供产品的类库,并且只想暴露它们的接口,而不是它们的实现。
- 从概念上讲,依赖项的生存期比使用者的生存期短。
- 你需要一个运行时值来构建特定的依赖关系
- 你想决定在运行时从系列中调用哪种产品。
- 你需要提供一个或更多仅在运行时才知道的参数,然后才能解决依赖关系。
- 当你需要产品之间的一致性时
- 在向程序添加新产品或产品系列时,您不想更改现有代码。
示例场景
- 在运行时在
FileSystemAcmeService
,DatabaseAcmeService
或NetworkAcmeService
中选择并调用一个
- 单元测试用例的编写变得更加容易
- 适用于不同操作系统的UI工具
影响
Java中的依赖注入会隐藏服务类的依赖关系,这些依赖关系可能导致运行时错误,而这些错误在编译时会被捕获。
虽然在创建预定义对象时模式很好,但是添加新对象可能会很困难。
由于引入了许多新的接口和类,因此代码变得比应有的复杂。
已知使用
- javax.xml.parsers.DocumentBuilderFactoryopen in new window
- javax.xml.transform.TransformerFactoryopen in new window
- javax.xml.xpath.XPathFactoryopen in new window