简介
抽象工厂模式(Abstract Factory Pattern)隶属于设计模式中的创建型模式,用于产品族的构建。
定义
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。(可以认为抽象工厂是对具有同一主题的一组单独工厂方法的封装)
抽象工厂模式中有一个产品族的概念,理解了产品族就算是抽象工厂入门了。所谓产品族,就是一系列相关的产品对象组成的一族对象,它们是独立的同时也是互相关联的,它们共同组合在一起可以变成一个更大的产品。举例来说,Mac 电脑里所有的 UI 组件是一个产品族,存在 MacButton
MacText
等组件,对应的 Windows 电脑里所有的 UI 组件也组成了一个产品族,存在 WindowsButton
, WindowsText
等组件,两个产品族里的产品是不同混合使用的。
角色
抽象工厂模式中存在四种角色,跟工厂方法类似,分别是抽象工厂接口,具体工厂实现,抽象产品接口,具体产品实现。
不同之处在于抽象产品接口会存在多个,而且它们之间具有内在的相关关系。
模式结构
在工厂方法模式中,我们举了造车的例子,其实造车这个例子扩展一下也非常适合于讲解抽象工厂模式;上面在说明产品族时举了Mac 和 Windows 电脑的例子。这里我们使用组装电脑来演示。
现在组装电脑不多见了,一般都是笔记本或一体机,以前大家去卖场里单独买 CPU,主板,显示器等组件可以组装出一台电脑来,在我们的系统里,我们有各种组件,用户可以选择不同的组件来组装自己喜欢的电脑。
但有一点需要注意,主板和 CPU 是需要兼容才能使用的,不是随便搭在一起就成为一台电脑了。
// 产品接口
public interface MainBoard {
public void setCpu();
public void run();
}
public interface Cpu {
public void doLogic();
}
// 具体产品实现
public class IntelMainBoard implements MainBoard {
private Cpu cpu;
public void run() {
System.out.println("IntelMainBoard is running.");
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
}
public class AmdMainBoard implements MainBoard {
private Cpu cpu;
public void run() {
System.out.println("AmdMainBoard is running.");
}
public void setCpu(Cpu cpu) {
this.cpu = cpu;
}
}
public class IntelCpu implements Cpu {
public void doLogic() {
System.out.println("IntelCpu is doing logic");
}
}
public class AmdCpu implements Cpu {
public void doLogic() {
System.out.println("AmdCpu is doing logic");
}
}
如果由用户自行组装,很可能由于Amd 的主板使用了 Intel 的CPU,导致组装起来的电脑不能正常运行,而且这些细节也不需要用户操心。
此时我们的抽象工厂模式就登场了,抽象工厂就像是卖场工作人员提供给你的几种套餐一样,定义了组装电脑的套餐,用户可以直接使用套餐里的组件然后组装成电脑,这样就绝对不会出现兼容问题了。
// 抽象工厂接口
public interface ComponentAbstractFactory {
public MainBoard getMainBoard();
public Cpu getCpu();
}
// 具体工厂实现
// Intel 系列
public class IntelAbstractFactory implements ComponentAbstractFactory {
public MainBoard getMainBoard() {
return new IntelMainBoard();
}
public Cpu getCpu() {
return new IntelCpu();
}
}
// Amd 系列电脑
public class AmdAbstractFactory implements ComponentAbstractFactory {
public MainBoard getMainBoard() {
return new AmdMainBoard();
}
public Cpu getCpu() {
return new AmdCpu();
}
}
有了套餐用户不需要担心不兼容,可以愉快的组装电脑然后使用了。
// 电脑接口
public interface Computer {
// 装好主板,cpu 装在主板上
public void setMainBoard(MainBoard mainBoard);
//其他的需要放置的东西
public void otherSettings();
public void run();
}
// 组装电脑
public class ComboComputer implements Computer {
// 装好主板,cpu 装在主板上
public void setMainBoard(MainBoard mainBoard){
//..
}
//其他的需要设置的东西
public void otherSettings() {
//....
}
public void run() {
mainBoard.run();
}
}
public class Client {
public static void main(String[] args) {
Computer computer = new ComboComputer();
ComponentAbstractFactory factory = new AmdAbstractFactory();
MainBoard mainBoard = factory.getMainBoard();
Cpu cpu = factory.getCpu();
mainBoard.setCpu(cpu);
computer.setMainBoard(mainBoard);
computer.run();
}
}
大家有没有发现,用户为了组装一台电脑出来,虽然不需要了解cpu 厂家之类的细节的,还是要了解cpu 怎么装到主板上,主板又怎么装到电脑上,然后才能得到一台完整的电脑,这里又可以用到前一节说的工厂方法模式来帮助用户将电脑组装起来了。
抽象工厂最初是来自操作系统之间的UI 构建,每个系统有自己的一套 UI界面,现在这个限制已经不是很强了,就像Intel 的主板也可以装载AMD 的CPU,这通过再实现一个抽象工厂类就可以了,还有其他的例子譬如电脑的主题,里面包含字体、背景、策略等一系列产品构成的产品族也适合于使用抽象工厂模式。
使用场景
- 适合于产品之间相互关联、相互依赖且相互约束的地方
- 需要动态切换产品族的地方
优缺点
优点:
- 抽象工厂模式将产品族的依赖与约束关系放到抽象工厂中,便于管理。
- 职责解耦,用户不需要关心一堆自己不关心的细节,由抽象工厂来负责组件的创建
- 切换产品族容易,只需要增加一个具体工厂实现,客户端选择另一个套餐就可以了
缺点:
- 抽象工厂模式类增加的速度很快,有一个产品族就需要增加一个具体工厂实现,比较繁琐
- 产品族难以扩展产品。当产品族中增加一个产品时,抽象工厂接口中需要增加一个函数,对应的所有具体工厂实现都需要修改,修改放大严重。
- 抽象工厂并未完全屏蔽创建细节,给出的都是组件。对于这种情况可以结合工厂模式或简单工厂模式一起使用。
最佳实践
- 大家应该已经发现了,其实抽象工厂模式如果只有一个组件的话,其实是退化到了工厂方法模式,也就是没有了产品族的概念,只剩一个产品了,因此简单工厂,工厂方法,抽象工厂这三者之间是有内在联系的,区别只是产品的复杂度。
- 抽象工厂的本质是选择产品族,因此大家可以根据这个特征来识别是否可以应用抽象工厂。