抽象工厂模式(Abstract Factory Pattern)是一种比较常用的模式,其定义如下:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes。
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定它们的具体类。
举一个电脑产品的例子吧:
IBM,Dell都是著名的计算机生产厂家,他们采用的主板、硬盘及CPU是不相同的,但是配件间、主板与CPU一定要相互兼容,例如下面例子中的微星MSIK7N2G配AMD的CPU;微星MSI865PE配Intel的CPU。
如图中所示,ComputerFactory是抽象工厂,Dell和IBM是生产产品的工厂;CPU、HardDisk、MainBoard是抽象产品,CPU的型号又分许多种。
uml关系图如下:
具体实现见代码:
首先,定义CPU的接口:
public interface CPU {
String getCPU();
}
定义AMD类,实现CPU接口:
public class AMD implements CPU {
@Override
public String getCPU() {
return "Athlon XP 2008+";
}
}
定义Intel类,实现CPU接口:
public class Intel implements CPU{
@Override
public String getCPU() {
return "奔腾4 3.2C";
}
}
定义硬盘接口:
public interface HardDisk {
String getSize();
}
定义Maxtor类,实现硬盘接口:
public class Maxtor implements HardDisk{
@Override
public String getSize() {
// TODO Auto-generated method stub
return "MaXLine Plus II 200G";
}
}
定义WestDigit,实现硬盘接口:
public class WestDigit implements HardDisk{
@Override
public String getSize() {
return "WD2500JD 250G";
}
}
定义主板的接口,包含参数为CPU的公共方法Attach():
public interface MainBoard {
void Attach(CPU cpu) throws Exception;
}
主板微星MSI865PE,支持Intel的CPU:
public class MSI865PE implements MainBoard{
@Override
public void Attach(CPU cpu) throws Exception {
if(cpu.getClass ().toString ().endsWith("Intel")){
System.out.println("MSI865PE");
}
else{
throw new Exception("主板MSI865PE只能配Intel的CPU");
}
}
}
主板微星MSIK7N2G,支持AMD的CPU:
public class MSIK7N2G implements MainBoard {
@Override
public void Attach(CPU cpu) throws Exception {
if(cpu.getClass ().toString ().endsWith("AMD")){
System.out.println("MSIK7N2G");
}
else{
throw new Exception("主板MSIK7N2G只能配AMD的CPU");
}
}
}
定义抽象电脑工厂类:
public abstract class ComputerFactory {
CPU cpu;
HardDisk hd;
MainBoard mb;
public void show() {
try {
System.out.println(this.getClass().getName().toString() + ("生产的电脑配置"));
System.out.println("CPU:" + cpu.getCPU());
System.out.println("HardDisk:" + hd.getSize());
System.out.print("MainBoard:");
mb.Attach(cpu);
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
抽象电脑工厂类派生类IBM,定义其返回的系列配件产品:
public class IBM extends ComputerFactory {
public IBM() {
cpu = new Intel();
hd = new WestDigit();
mb = new MSI865PE();
}
}
抽象电脑工厂类派生类Dell,定义其返回的系列配件产品:
public class Dell extends ComputerFactory {
public Dell(){
cpu = new AMD();
hd = new Maxtor();
mb = new MSIK7N2G();
}
}
客户程序调用:
public class Client {
public static void main(String[] args) {
IBM ibm = new IBM();
ibm.show();
Dell dell = new Dell();
dell.show();
}
}
输出结果为:
Computerworld.IBM生产的电脑配置
CPU:奔腾4 3.2C
HardDisk:WD2500JD 250G
MainBoard:MSI865PE
Computerworld.Dell生产的电脑配置
CPU:Athlon XP 2800+
HardDisk:MaXLine Plus II 200G
MainBoard:MSIK7N2G
抽象工厂模式的缺点
抽象工厂模式的最大缺点就是产品族扩展非常困难,为什么这么说呢?我们以通用代码为例,如果要增加一个产品C,也就是说有产品家族由原来的2个,增加到3 个,看看我们的程序有多大改动吧!抽象类AbstractCreator要增加一个方法createProductC(),然后,两个实现类都要修改,想 想看,这在项目中的话,还这么让人活!严重违反了开闭原则,而且我们一直说明抽象类和接口是一个契约,改变契约,所有与契约有关系的代码都要修改,这段代 码叫什么?叫“有毒代码”,——只要这段代码有关系,就可能产生侵害的危险!
最佳实践
一个模式在什么情况下才能够使用,是我刚开始比较困惑的地方,抽象工厂模式是一个简单的模式,使用的场景非常多,在软件产品开发过程中,涉及到不同操 作系统的时候,都可以考虑使用抽象工厂模式,例如一个应用,需要在三个不同平台上运行:Windows、Linux、Android(Google发布的 智能终端操作系统)上运行,你会怎么设计?分别设计三套不同的应用?非也非也,通过抽象工厂模式屏蔽掉操作系统对应用的影响。三个不同操作系统上的软件功 能、应用逻辑、UI都应该是非常类似,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息。
与工厂模式区别:
工厂模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。