抽象工厂模式
原文链接:Abstract Factory Pattern Tutorial With Java Examples
工厂模式包括简单工厂模式,工厂方法模式以及抽象工厂模式,本篇主要介绍抽象工厂模式。
现实世界中的工厂
考虑现实世界的工厂非常简单-工厂即为生产汽车、电脑或者电视等物品的地方。维基百科关于现实世界中的工厂的定义如下:
工厂又称制造厂,是一所用以生产货物的大型工业楼宇。大部分工厂皆设有大型机器或设备构成的生产线。
工厂的定义很简单,那么设计模式中的工厂如何工作呢?
抽象工厂模式
抽象工厂模式是建造者模式的一种-该模式使得具体对象的创建与客户程序解耦。我们先看一下抽象工厂模式的类图:

虽然抽象工厂模式的概念比较简单,但还是有必要研究一下上述类图。我特意将ConcreteFactory2负责的部分标红了。AbstractFactory定义了所有具体工厂需要实现的接口,具体工厂只有实现了该接口才能够生产Products。ConcreteFactory1和ConcreteFactory1均实现了该接口,这两座工厂分别用于生产不同的产品族。
这里以袜子为例顺便解释一下我对产品族的理解:比如ConcreteFactory1为玉珠集团,ConcreteFactory1为杨氏集团,玉珠袜业生产玉珠短袜以及玉珠手套,而杨氏集团生产杨氏短袜和杨氏手套。这里玉珠短袜、玉珠手套以及杨氏短袜、杨氏手套分别由不同的工厂生产,即为不同的产品族。
接着讨论抽象工厂模式。AbstractProductA和AbstractProductB分别是不同大类的产品的接口,这里我想用大类这个名词来区分短袜和手套这两种不同的产品。每个具体工厂都会生产这两大类产品,只不过每个工厂都会生产大类下面的具体产品。
Client仅针对AbstractFactory、AbstractProductA和AbstractProductB编程,而对任何具体类型透明。Client真正使用的具体工厂在运行时确定,这点会在后面的Java程序实现中体现出来。正如你所看到的,这个模式的一个优点就是Client是与具体Product解耦的。如果想要添加一个新的产品族,则需要添加一个实现了AbstractFactory接口的ConcreteFactory,同时还需要创建与该具体工厂对应的具体产品。
从Client的角度来看,我仅针对接口编程,因此可以保证自身代码不依赖于具体类型,与具体类型解耦。
抽象工厂模式用在哪里?
如果你的系统中需要创建不同的产品族,则可以使用抽象工厂模式。比如你想让你的UI家族(windows, buttons, textfields等等)支持不同的操作系统(跨平台),那么使用抽象工厂模式可以使你的客户端代码与具体的平台解耦。现在我们使用Java程序实现一个生成window的例子:
首先创建Window接口,这里Window是一种AbstractProduct:
// Our AbstractProductA
public interface Window {
public void setTitle(String text);
public void repaint();
}
接着创建Window的两种不同的具体实现,作为ConcreteProduct:
// ProductA1 微软Windows系统的窗口部件
public class MSWindow implements Window {
public void setTitle() {
// MS Windows specific behaviour
}
public void repaint() {
// MS Windows specific behaviour
}
}
// ProductA2 苹果OSX系统的窗口部件
public class MacOSXWindow implements Window {
public void setTitle() {
// Mac OSX specific behaviour
}
public void repaint() {
// Mac OSX specific behaviour
}
}
现在我们来实现工厂。首先定义AbstractFactory,这里我们仅要求工厂能够生产window(窗口),当然也可以生成其他大类的东西,不过我们简化处理:
// AbstractFactory
public class AbstractWidgetFactory {
public Window createWindow();
// ... of course we can create other types
}
接着我们针对微软和苹果两个操作系统实现两座具体工厂:
// ConcreteFactory1
public class MSWindowsWidgetFactory {
public Window createWindow() {
MSWindow window = new MSWindow();
return window;
}
}
// ConcreteFactory2
public class MacOSXWidgetFactory {
public Window createWindow() {
MacOSXWindow window = new MacOSXWindow();
return window;
}
}
最后我们针对接口编程,实现Client程序:
// Client
public class GUIBuilder {
public void buildWindow(AbstractWidgetFactory widgetFactory) {
Window window = widgetFactory.createWindow();
window.setTitle("New Window");
}
}
Client真正使用的具体工厂在运行时确定:
public class Main {
public static void main(String[] args) {
GUIBuilder builder = new GUIBuilder();
AbstractWidgetFactory widgetFactory = null;
// check what platform we're on
if (Platform.currentPlatform() == "MACOSX") {
widgetFactory = new MacOSXWidgetFactory();
}
else {
widgetFactory = new MSWindowWidgetFactory();
}
builder.buildWindow(widgetFactory);
}
}
这里给出本实现的抽象工厂模式的类图:

抽象工厂模式的缺点
该模式将Client和具体实现解耦是其核心优点,但是有时候需要在抽象类(AbstractFactory, AbstractProduct)中新增属性,这样修改抽象类和Client代码不可避免。