《研磨设计模式》之工厂方法模式
一、应用场景
实现一个能导出数据到各种文件格式(txt,DBFile,Excel,Xml)的应用框架.
1.不管用户导出神马样的文件格式,都要进行一个导出的操作.
2.系统不知道用户会导出哪种格式的文件
书中描述,框架是一个半成品,开发者可以在此基础上完成软件功能。因此具体是导出神马样的格式,是由具体的开发者(本例是Client类)决定的.
根据1.都要进行一个导出操作,因此要定义具有导出功能的接口
1: /**2: * 定义数据导出的功能的接口3: * @author YoungCold4: *5: */6: public interface ExportFileApi {7: /**8: * 将数据导出成文件(具体文件格式有实现类决定)9: * @param data10: * @return11: */12: public boolean export(String data);13: }
对于导出数据的业务功能对象,也就是用ExportFileApi定义的导出数据的方法export()完成导出数据这一功能的类的实例.它需要根据用户的需要(比如,用户选择导出数据到Excel文件)创建相应的ExportFileApi对象(实现ExportFileApi定义的export():将数据导出到Excel),也就是说特定的ExportFileApi实现由实际情况决定。问题是,业务功能对象不知道如何创建ExportFileApi的对象(实际上,也不需要知道)
二、工厂模式
工厂方法的定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类①,Factory Method使一个类②的实例化延迟到子类。
补充:就对应用场景而言,个人感觉,①和②指的是同一个类——实现ExportFileApi的类。
前面提过,实现导出功能的业务类跟本不知道实际会选择哪种导出数据的格式,因此该对象就不应该和具体的导出文件对象(实现ExportFileApi的类)耦合在一起,只需要知道ExportFileApi接口即可。这样一来,业务类要求不能和ExportFileApi具体实现类耦合,但业务类同时要实现导出数据的功能,这就必然要用到ExportFileApi具体实现类的功能。
解决方法:既然需要接口对象,那就定义一个方法创建接口对象。但事实上,业务类并不知道要创建哪种接口对象,因此创建接口对象的任务就由业务类的子类完成,比方说是导出txt文件的业务类(业务类的子类)完成,这样导出txt文件的业务类负责创建出接口对象,该接口对象完成数据导出到txt的功能。所以,业务类的创建接口对象的方法就定义成抽象方法。
1.实现导出功能的业务类是个抽象类2.具体的导出操作有业务类的子类完成数据导出功能业务类1: /**2: * 实现导出数据的业务功能对象3: * 即使用数据导出的工具类,实现实际的数据导出4: * @author YoungCold5: *6: */7: public abstract class ExportOperate {8: public boolean export(String data){9: ExportFileApi exporter = createExportFileApi();10: return exporter.export(data);11: }12:13: public abstract ExportFileApi createExportFileApi();14: }
数据导出功能业务类的子类,实现具体的导出功能(创建何种接口对象)
接口对象(实现数据导出到txt文件)1: /**2: * 导出数据的工具类,实现数据导出到文本文件3: * @author YoungCold4: *5: */6: public class ExportTxtFile implements ExportFileApi {7:8: @Override9: public boolean export(String data) {10: System.out.println("导出数据"+data+"到文本文件");11: return true;12: }13:14: }
接口对象(实现数据导出到数据库文件)1: /**2: * 导出数据的工具类,实现数据导出到数据库文件3: * @author YoungCold4: *5: */6: public class ExportDBFile implements ExportFileApi {7:8: @Override9: public boolean export(String data) {10: System.out.println("导出数据"+data+"到数据库文件");11: return true;12: }13:14: }
数据导出业务类(负责创建 数据导出到txt文件的接口对象)1: public class ExportTxtFileOperate extends ExportOperate {2:3: @Override4: public ExportFileApi createExportFileApi() {5: // 创建数据导出到txt文件的接口对象6: return new ExportTxtFile();7: }8:9: }
数据导出业务类(负责创建 数据导出到数据库文件的接口对象)1: public class ExportDBFileOperate extends ExportOperate {2:3: @Override4: public ExportFileApi createExportFileApi() {5: // 创建数据导出到数据库文件的接口对象6: return new ExportDBFile();7: }8:9: }
这样,当用户选择数据导出到具体格式的文件时,我们只需要创建这种文件格式的业务类的对象即可。
比如,用户选择数据导出到数据库文件
1: String data = "2013-1-13";//要导出的数据2: ExportOperate exporter1 = new ExportDBFileOperate();//3: exporter1.export(data);//
用户选择数据导出到txt文件
1: String data = "2013-1-13";2: ExportOperate exporter2 = new ExportTxtFileOperate();//3: exporter2.export(data);
再来看数据导出业务抽象类,所实现的数据导出方法1: public boolean export(String data){2:3: return exporter.export(data);4: }ExportFileApi exporter = createExportFileApi();业务类的导出功能代码中,没有和具体的接口对象耦合,根本不知道创建的哪一个接口对象,它只关心得到一个接口对象,再调用接口对象的导出方法,完成数据导出功能。
三、工厂方法的结构
Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象接口 ExportFileApi
Creator:创建器,声明工厂方法,工厂方法通常会返回一个Product类的实例对象,而且多是抽象方法。也可以在Creator里面提供工厂方法的默认实现,让工厂方法返回一个缺省的Product类型的实例对象 ExportOperate
ConcreteProduct:具体Product接口的实现对象 ExportTxtFile ExportDBFile
ConcreteCreator:具体的创建对象,覆盖实现Creato定义的工厂方法,返回具体的Product实例 ExportTxtFileOperate ExportDBFileOperate