设计模式---------抽象工厂模式

来源:互联网 发布:软件下载网 编辑:程序博客网 时间:2024/05/20 17:09

前面降到了简单工厂模式和工厂方法模式,首先来看看这两者的定义区别:

工厂模式:定义了一个用于创建对象的借口,让子类决定实例化哪一个类。

抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类

      个人觉得这个区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。

      举一个生活中常见的例子——组装电脑,我们在组装电脑时,需要给工作人员列出选择的一系列配件,如CPU、主板、磁盘、内存等,为了讨论方便,只考虑选择CPU和主板的问题。

      我们在选择CPU时,面临一系列的问题,比如品牌、芯片组、针脚数目、主频等,同样的,选择主板也面临着一系类问题。客户在组装电脑的时候向工作人员提出装机方案,在最终确定方案之前,还需要考虑配件之间的兼容性,在这里以CPU的针脚数和主板提供的CPU插槽孔数为例。

      对客户而言,语言选择自己需要的CPU和主板,然后告诉装机工作人员,接下来就是装机工作人员的事情了。

      对装机工作人员而言,只知道CPU和主板的接口,而不知道具体的实现,很明显可以用到简单工厂或工厂方法模式了,在这里我们选择简单工厂。

1、下面来看一下CPU和主板的接口

/** * cpu接口定义 */public interface CPUApi {/** * cpu具有运算功能 */public void calculate();}
/** * 主板接口定义 */public interface MainBoardApi {/** * 具有安装CPU的功能 */public void installCPU();}

2、cpu和主板的具体实现

(1)Intel CPU的实现代码:

/** *intel的cpu实现 */public class IntelCPU implements CPUApi{/** * CPU的针脚数 */private int pins = 0;public IntelCPU(int pins) {this.pins = pins;}@Overridepublic void calculate() {System.out.println("now in Intel CPU, pins=" + pins);}}

(2)AMD 的CPU实现

/** *amd的CPU实现 */public class AMDCPU implements CPUApi {/** * CPU的针脚数 */private int pins = 0;public AMDCPU(int pins) {this.pins = pins;}@Overridepublic void calculate() {System.out.println("now in AMD CPU, pins=" + pins);}}

(3)技嘉的主板实现

public class GAMainBoard implements MainBoardApi {/** * CPU插槽的孔数 */private int cpuHoles = 0;public GAMainBoard(int cpuHoles) {this.cpuHoles = cpuHoles;}@Overridepublic void installCPU() {System.out.println("now in GA MainBoard, cpuHoles=" + cpuHoles);}}

(4)微星的主板实现

public class MSIMainBoard implements MainBoardApi {/** * CPU插槽的孔数 */private int cpuHoles = 0;public MSIMainBoard(int cpuHoles) {this.cpuHoles = cpuHoles;}@Overridepublic void installCPU() {System.out.println("now in MSI MainBoard, cpuHoles=" + cpuHoles);}}

3、创建CPU和主板的工厂

(1)创建CPU的工厂实现

public class CPUFactory {/** * 创建CPU接口对象的方法 * @param type 选择CPU类型的参数 * @return CPU接口对象的方法 */public static CPUApi createCPUApi(int type) {CPUApi cpu = null;if (type == 1) {cpu = new IntelCPU(1156);} else if (type == 2) {cpu = new AMDCPU(939);}return cpu;}}

(2)创建主板的简单工厂

public class MainBoardFactory {/** * 创建MainBoard接口对象的方法 * @param type 选择MainBoard类型的参数 * @return MainBoard接口对象的方法 */public static MainBoardApi createMainBoardApi(int type) {MainBoardApi mianBoard = null;if (type == 1) {mianBoard = new GAMainBoard(1156);} else if (type == 2) {mianBoard = new MSIMainBoard(939);}return mianBoard;}}

4、装机工程师的实现

public class ComputerEngineer {private CPUApi cpu = null;private MainBoardApi mainBoard = null;/** * 模拟装机过程 * @param cpuType 客户选择cpu的类型 * @param mainBoardType 客户选择主板的类型 */public void makeComputer(int cpuType, int mainBoardType) {//1、首先准备好所需的配件prepareHardwares(cpuType, mainBoardType);//2、组装机器//3、测试机器//4、交付客户}/** * 准备装机所需的配件 * @param cpuType 客户选择cpu的类型 * @param mainBoardType 客户选择主板的类型 */private void prepareHardwares(int cpuType, int mainBoardType) {//装机工程师并不知道如何去获取配件实例,这样就需要工厂来获取this.cpu = CPUFactory.createCPUApi(cpuType);this.mainBoard = MainBoardFactory.createMainBoardApi(mainBoardType);//测试配件是否可用this.cpu.calculate();this.mainBoard.installCPU();}}

5、客户通过装机工程师组装电脑,客户需要告诉装机工程师他选择的配件。

public class Client {public static void main(String[] args) {ComputerEngineer engineer = new ComputerEngineer();//告诉装机工程师自己选择的配件engineer.makeComputer(1, 1);}}

运行结果如下:

now in Intel CPU, pins=1156

now in GA MainBoard, cpuHoles=1156

上面的实现,虽然通过简单工厂实现,但还有一个问题没有解决,还需要考虑CPU对象和主板对象其实是有关系的,需要相互匹配,上述的实现并没有考虑这种关系,客户可以任意选择,比如在上述实现的客户端,在调用makeComputer时,传入的参数为(1,2),其运行结果为:

now in Intel CPU, pins=1156

now in MSI MainBoard, cpuHoles=939

很显然CPU的针脚数和主板的插槽数不匹配,无法组装,这是由于没有维护配件之间的关系造成的,那么该怎么解决这个问题那?

为了解决上述问题,可以提供一个创建一系列相互依赖对象的接口,然后再具体的实现该接口(提供兼容的方案),也就是我们通常所说的抽象工厂方法。

这样的话装机客户就会提出他们自己的具体装机方案,或者选择已有的装机方案,也就相当于抽象工厂提供了具体的子类实现,在这些具体的实现中,会创建兼容的CPU和主板对象,使用UML画出该示例的结构示意图。

下面我们来重写上述示例,前面示例实现的CPU和主板接口和实现对象,还有主板的接口和实现对象都不需要变化,这里不再赘述,需要重新定义一个抽象工厂,代码示例如下:

public interface AbstractFactory {/** * 创建CPU接口对象的方法 * @return CPU接口对象的方法 */public CPUApi createCPUApi();/** * 创建MainBoard接口对象的方法 * @return MainBoard接口对象的方法 */public MainBoardApi createMainBoardApi();}

再来看看抽象工厂的具体实现,也就是具体的装机方案,在这里提供两种装机方案:

/** * 装机方案一:intel cpu + ga mainboard */public class Schema1 implements AbstractFactory {@Overridepublic CPUApi createCPUApi() {return new IntelCPU(1156);}@Overridepublic MainBoardApi createMainBoardApi() {return new GAMainBoard(1156);}}
/** * 装机方案二:amd cpu + msi mainboard */public class Schema2 implements AbstractFactory {@Overridepublic CPUApi createCPUApi() {return new AMDCPU(939);}@Overridepublic MainBoardApi createMainBoardApi() {return new MSIMainBoard(939);}}

然后需要修改的就是装机工程师的实现,在这里装机工程师相当于使用抽象工厂的客户端,与前面的实现相比,该类不再需要客户传入选择CPU和主板的参数,而是直接传入客户选择的装机方案对象,其示例代码如下:

public class ComputerEngineer {private CPUApi cpu = null;private MainBoardApi mainBoard = null;public void makeComputer(AbstractFactory schema) {//1、首先准备好所需的配件prepareHardwares(schema);//2、组装机器//3、测试机器//4、交付客户}/** * 准备装机所需的配件 */private void prepareHardwares(AbstractFactory schema) {//装机工程师并不知道如何去获取配件实例,这样就需要抽象工厂来获取this.cpu = schema.createCPUApi();this.mainBoard = schema.createMainBoardApi();//测试配件是否可用this.cpu.calculate();this.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 {/** * 一个通用的创建产品的方法 * @param type 具体创建产品类型标示 * @return 创建出的产品对象 */public Object createProduct(int type);}

相应的它的具体实现(方案)为:

/** * 装机方案一:intel cpu + ga mainboard */public class Schema1 implements AbstractFactory {@Overridepublic Object createProduct(int type) {Object schemaObj = null;//type=1为创建cpu,type=2为创建mianboardif(type == 1) {schemaObj = new IntelCPU(1156);} else if(type == 2) {schemaObj = new GAMainBoard(1156);}return schemaObj;}}
/** * 装机方案二:amd cpu + msi mainboard */public class Schema2 implements AbstractFactory {@Overridepublic Object createProduct(int type) {Object schemaObj = null;//type=1为创建cpu,type=2为创建mianboardif(type == 1) {schemaObj = new AMDCPU(939);} else if(type == 2) {schemaObj = new MSIMainBoard(939);}return schemaObj;}}
这时候抽象工厂的客户端,也就是装机工程师类里面,通过抽象工厂获取相应的配件:
public class ComputerEngineer {private CPUApi cpu = null;private MainBoardApi mainBoard = null;public void makeComputer(AbstractFactory schema) {//1、首先准备好所需的配件prepareHardwares(schema);//2、组装机器//3、测试机器//4、交付客户}/** * 准备装机所需的配件 */private void prepareHardwares(AbstractFactory schema) {//装机工程师并不知道如何去获取配件实例,这样就需要抽象工厂来获取this.cpu = (CPUApi) schema.createProduct(1);this.mainBoard = (MainBoardApi) schema.createProduct(2);//测试配件是否可用this.cpu.calculate();this.mainBoard.installCPU();}}

当然,上面的这种方式存在不安全的部分,你会发现创建产品对象返回来之后,需要强转为具体的对象,如果这时候没有匹配上,比如返回的不是CPU对象,但需要强转为CPU类型,就会发生错误,这就是这个方式不安全的地方。

下面来看看这种方式的灵活性,也就是可扩展性,先实现一个内存接口和一个该内存接口的具体实现类(HyMemory),在这里就不一一给出代码了。

现在若使用这种新加入的产品,只需要添加一个新的方案就可以了,其代码示例为:

/** * 装机方案三:intel cpu + ga mainboard + hy memory */public class Schema3 implements AbstractFactory {@Overridepublic Object createProduct(int type) {Object schemaObj = null;//type=1为创建cpu,type=2为创建mianboard,type=3为创建memoryif(type == 1) {schemaObj = new AMDCPU(1156);} else if(type == 2) {schemaObj = new MSIMainBoard(1156);} else if(type == 3) {schemaObj = new HyMemory();}return schemaObj;}}

这时候的装机工程师类,如果创建带内存的电脑,需要在装机工程师类里面添加对内存的使用。

public class ComputerEngineer {private CPUApi cpu = null;private MainBoardApi mainBoard = null;private MemoryApi memory = null;public void makeComputer(AbstractFactory schema) {//1、首先准备好所需的配件prepareHardwares(schema);//2、组装机器//3、测试机器//4、交付客户}/** * 准备装机所需的配件 */private void prepareHardwares(AbstractFactory schema) {//装机工程师并不知道如何去获取配件实例,这样就需要抽象工厂来获取this.cpu = (CPUApi) schema.createProduct(1);this.mainBoard = (MainBoardApi) schema.createProduct(2);this.memory = (MemoryApi) schema.createProduct(3);//测试配件是否可用this.cpu.calculate();this.mainBoard.installCPU();//为了满足以前和现在的客户端要求if(memory != null) {this.memory.cacheData();}}}

这里,内存操作的地方跟前面的CPU和主板不一样,多了一个if判断语句,原因是需要满足以前和现在的客户端的需要,如果以前的客户端没有使用内存,就会出错,这个时候添加一个判断句,有内存的时候才操作内存,就不会出错。

此时的客户端代码示例:

public class Client {public static void main(String[] args) {ComputerEngineer engineer = new ComputerEngineer();//告诉装机工程师自己选择的装机方案AbstractFactory1 schema = new Schema3();engineer.makeComputer(schema);}}

运行结果如下:

now in AMD CPU, pins=1156

now in MSI MainBoard, cpuHoles=1156

now in HyMemory

这种方式,灵活多了吧,当然前面讲到了,这种方式可能不太安全。

综上所述,抽象工厂的本质就是选择产品簇的实现。它具有方便分离接口和实现、使得切换产品簇变得容易等优点,当然,也存在不易扩展和容易造成类层次结构复杂的缺点。








0 0
原创粉丝点击