定义:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
结构:
抽象工厂模式中的角色和工厂方法模式的完全相同,由四个部分组成:
-
抽象工厂:工厂方法模式的核心,与应用程序无关。是具体工厂角色必须实现的接口或必须继承的父类
-
具体工厂:由应用程序调用以创建对应具体的产品的对象
-
抽象产品:产品接口
-
具体产品:具体的产品实现对象,通常在具体工厂里,会选择具体的产品实现对象,来创建符合抽象工厂定义的方法返回的产品类型的对象
场景实例:组装电脑,选择组装电脑的配件
客户选择不同的CPU和主板,在最终确定装机方案之前,还需整体考虑各个配件之间的兼容性。比如CPU针脚数和主板提供的CPU插口不兼容,是无法组装的。也就是说,装机方案是有整体性的,里面选择的各个配件之间是有关联的
简单起见,使用简单工厂实现:
- 抽象接口
/** * CPU接口<产品簇1> */ public interface CPUApi { //实例方法,CPU运算的功能 public void calculate(); } /** * 主板接口<产品簇2> */ public interface MainboardApi { //实例方法,安装CPU的功能 public void installCPU(); }
- 具体产品实现
class IntelCPU implements CPUApi{ //CPU针脚数 private int pins=0; public IntelCPU(int pins){ this.pins=pins; } @Override public void calculate() { System.out.println("now in Intel CPU ,pins="+pins); } } class AMDCPU implements CPUApi{ //CPU针脚数 private int pins=0; public AMDCPU(int pins){ this.pins=pins; } @Override public void calculate() { System.out.println("now in AMD CPU ,pins="+pins); } } /** * Intel主板 */ class IntelMainboard implements MainboardApi{ //CPU插槽孔数 private int cpuHoles = 0; public IntelMainboard(int cpuHoles){ this.cpuHoles=cpuHoles; } @Override public void installCPU() { System.out.println("Intel 主板的CPU插槽孔数是:"+cpuHoles); } } /** * AMD主板 */ class AMDMainboard implements MainboardApi{ //CPU插槽孔数 private int cpuHoles = 0; public AMDMainboard(int cpuHoles){ this.cpuHoles=cpuHoles; } @Override public void installCPU() { System.out.println("AMD 主板的CPU插槽孔数是:"+cpuHoles); } }
-
工厂方法
/** * CPU工厂 */ public class CPUFactory { public static CPUApi createCPU(int type){ CPUApi cpu=null; if(type==1){ cpu=new IntelCPU(1156); }else{ cpu=new AMDCPU(939); } return cpu; } } /** * 主板工厂 */ class MainboardFactory { public static MainboardApi createMainboard(int type){ MainboardApi mainboard=null; if(type==1){ mainboard=new IntelMainboard(1156); }else{ mainboard=new AMDMainboard(939); } return mainboard; } }
-
装机工程师
/** * 装机工程师,不知道具体实现,只根据客户要求组装电脑 */ class ComputerEngineer{ private CPUApi cpu=null; private MainboardApi mainboard=null; //根据方案组装电脑 public void makeComputer(int cpuType,int mainboardType){ this.cpu=CPUFactory.createCPU(cpuType); this.mainboard=MainboardFactory.createMainboard(mainboardType); cpu.calculate(); mainboard.installCPU(); } }
-
客户端
public class Client { public static void main(String[] args) { //创建工程师对象 ComputerEngineer engineer=new ComputerEngineer(); //告诉工程师自己的方案,由工程师组装 engineer.makeComputer(1,2); //output: now in Intel CPU ,pins=1156 // AMD 主板的CPU插槽孔数是:28 } }
有何问题?
上面的实现虽然通过简单工厂解决了,但有一个问题没解决,那就是这些CPU对象和主板对象是有关系的,是需要互相匹配的。而上面的实现并没有维护这种关联关系,如客户端测试传入1,2参数就会导致无法组装。
抽象工厂模式来解决问题
- 抽象工厂
/** * 抽象工厂:声明创建抽象产品对象的操作 *@Author: April *@Date: 2014-7-5 */ public interface AbstractFactory { //创建CPU的对象 public CPUApi createCPUApi(); //创建主板的对象 public MainboardApi createMainboardApi(); }
-
抽象工厂的实现:即装机方案
/** * 装机方案一:Intel的CPU与Intel主板 */ public class Schema1 implements AbstractFactory{ @Override public CPUApi createCPUApi() { return new IntelCPU(1156); } @Override public MainboardApi createMainboardApi() { return new IntelMainboard(1156); } } /** * 装机方案二:AMD的CPU与AMD的主板 */ class Schema2 implements AbstractFactory{ @Override public CPUApi createCPUApi() { return new AMDCPU(939); } @Override public MainboardApi createMainboardApi() { return new AMDMainboard(939); } }
-
装机工程师
/** * 装机工程师,不知道具体实现,只根据客户要求组装电脑 */ class ComputerEngineer{ private CPUApi cpu=null; private MainboardApi mainboard=null; //根据方案组装电脑 public void makeComputer(AbstractFactory schema){ //使用抽象工厂获取相应的接口对象 this.cpu=schema.createCPUApi(); this.mainboard=schema.createMainboardApi(); //测试下.. cpu.calculate(); mainboard.installCPU(); } }
-
客户端
public class Client { public static void main(String[] args) { //创建工程师对象 ComputerEngineer engineer=new ComputerEngineer(); //客户选择并创建想要的装机方案 AbstractFactory schema=new Schema1(); engineer.makeComputer(schema); } }
定义可扩展的工厂
相对灵活但不太安全的改进方式:在抽象工厂中不需定义那么多方法,只定义一个方法,给这个方法设置一个参数,通过这个参数判断具体创建什么产品簇对象;这个方法的返回值就不能是具体某个产品类型了,可以是所有产品簇都实现的接口,也可以直接用Object类型
//抽象工厂修改 public interface AbstractFactory { public Object createProduct(int type); } //装机方案修改 public class Schema1 implements AbstractFactory{ @Override public Object createProduct(int type){ Object result=null; if(type==1){ result=new IntelCPU(1156); }else if (type==2){ result=new IntelMainboard(1156); } return result; } } //装机工程师修改 public class ComputerEngineer{ private CPUApi cpu=null; private MainboardApi mainboard=null; public void makeComputer(AbstractFactory schema){ //强制转换成接口对象,如果不匹配则会报错,所以不安全 this.cpu=(CPUApi)schema.createProduct(1); this.mainboard=(MainboardApi)schema.createProduct(2); cpu.calculate(); mainboard.installCPU(); } }
此时,如果要加入一个新的产品-内存,当然可以提供一个新的装机方案来使用它,这样已有的代码就不需要变化了
理解抽象工厂模式
-
抽象工厂的本质:选择产品簇的实现
-
抽象工厂的功能
为一系列相关对象或相互依赖的对象创建一个接口。注意:接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法,比如上面的CPU与主板,就是为组装一台电脑的相关对象
-
使用工厂方法
AbstractFactory定义的创建产品的方法可以看成是工厂方法,而这些工厂方法的具体实现就延迟到了具体的工厂里面。也就是说使用工厂方法来实现抽象工厂
- 切换产品簇
抽象工厂定义的是一个产品簇,这带来很大的灵活性,切换产品簇的时候,只需提供不同的抽象工厂实现就可以了,也就是说现在是以产品簇作为一个整体被切换
-
何时选用抽象工厂模式?
-
系统中有多个产品族,而系统一次只可能消费其中一族产品。【产品族概念:位于不同产品等级结构中,功能相关联的产品组成的家族。比如AMD的CPU和ADM芯片的主板,组成一个家族。Intel的CPU和Intel芯片的主板,又组成另一个家族】
-
同属于同一个产品族的产品一起使用。
-
-
优点:
-
分离接口和实现
-
使得切换产品簇变的容易
-
-
缺点:
-
不太容易扩展新产品。前面提到的扩展工厂方式可以但不够安全,所以需根据实际情况权衡。
-
容易造成类层次复杂
-