设计模式---------抽象工厂模式
来源:互联网 发布:软件下载网 编辑:程序博客网 时间: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
这种方式,灵活多了吧,当然前面讲到了,这种方式可能不太安全。
综上所述,抽象工厂的本质就是选择产品簇的实现。它具有方便分离接口和实现、使得切换产品簇变得容易等优点,当然,也存在不易扩展和容易造成类层次结构复杂的缺点。
- 抽象工厂设计模式
- 设计模式-----抽象工厂
- 抽象工厂设计模式
- 抽象工厂设计模式
- 抽象工厂设计模式
- 设计模式 抽象工厂
- 设计模式--抽象工厂
- 设计模式--抽象工厂
- 设计模式 抽象工厂
- 设计模式-抽象工厂
- 设计模式-> 抽象工厂
- 抽象工厂设计模式
- 设计模式--抽象工厂
- 【设计模式】抽象工厂
- 设计模式---抽象工厂
- 【设计模式-抽象工厂】
- 设计模式-抽象工厂
- 设计模式--抽象工厂
- XML文件要有根标签(Extra content at the end of the document in file 错误
- Android fragment onActivityResult 不起作用
- html中引入css的四种方法
- 命令行的shell
- extjs遇到的问题以及解决方式
- 设计模式---------抽象工厂模式
- windows 命令行 如何打开转换目录
- C# FTP简单帮助类
- ubuntu下安装英汉词典——stardict
- linux系统性能分析工具图解读
- 微软宣布.NET开发环境将开源 支持Mac OS X和linux
- 要做linux运维工程师的朋友,必须要掌握以下几个工具才行
- UVA - 10051 Tower of Cubes
- oracle 控制用户权限