一. 模式定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类,factory method使一个类的实例化延迟到其子类。
二. 结构和说明:
Product:定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口
ConcreteProduct:具体的Product接口的实现对象
Creator:创建器,声明工厂方法
ConcreteCreator:具体的创建器对象,覆盖实现Creator定义的工厂方法,返回具体的Product实例
三. 时序图:
四. 样例代码:
1. 创建器,声明工厂方法:
public abstract class Creator { // 创建Product的工厂方法 protected abstract Product factoryMethod(); /** * 示意方法,实现某些功能的方法 */ public void someOperation() { // 通常在这些方法实现中,需要调用工厂方法来获取Product对象 Product product = factoryMethod(); } }2. 具体的创建器实现对象:
public class ConcreteCreator extends Creator { public Product factoryMethod() { // 重定义工厂方法,返回一个具体的Product对象 return new ConcreteProduct(); } }3. 具体的Product对象:
public class ConcreteProduct implements Product { // 实现Product要求的方法 }4. 工厂方法所创建的对象的接口:
public interface Product { // 可以定义Product的方法 }
五. 实际案例:(不用模式)
现在我们需要实现一个导出数据的应用框架,让用户选择数据的导出方式,如:导出文本格式、导出数据库备份形式等。
现在我们采用两种实现方式:一种使用工厂方法模式,一种不使用工厂方法模式。
1. 导出的文件对象的接口:ExportFileApi
public interface ExportFileApi { /** * 导出内容成为文件 * @param data 示意:需要保存的数据 * @return 是否导出成功 */ public boolean export(String data); }2. 导出成文本文件格式的对象:ExportTxtFile
public class ExportTxtFile implements ExportFileApi{ public boolean export(String data) { //简单示意一下,这里需要操作文件 System.out.println("导出数据"+data+"到文本文件"); return true; } }3. 导出成数据库备份文件形式的对象:ExportDB
public class ExportDB implements ExportFileApi{ public boolean export(String data) { //简单示意一下,这里需要操作数据库和文件 System.out.println("导出数据"+data+"到数据库备份文件"); return true; } }4. 实现导出数据的业务功能对象:ExportOperate
public class ExportOperate { /** * 导出文件 * @param type 用户选择的导出类型 * @param data 需要保存的数据 * @return 是否成功导出文件 */ public boolean export(int type,String data){ // 先完成各种导出数据前的准备工作 System.out.println("进行数据校验"); System.out.println("进行数据转换"); System.out.println("进行数据格式的封装"); // 然后才真正的去导出 ExportFileApi api = null; // 根据类型来选择究竟要创建哪一种导出文件对象 if(type == 1){ api = new ExportTxtFile(); }else if(type == 2){ api = new ExportDB(); } return api.export(data); } }5. 客户端:client
public class Client { public static void main(String[] args) { ExportOperate operate = new ExportOperate(); operate.export(1, "要导出的测试数据"); } }
分析:我们可以看到在ExportOperate中需要判断传入的type类型来决定创建的对象,假设现在我们需要加一种导出xml的功能,那就得修改代码,再加一个判断。这显然不符合开闭原则:对添加开放,对修改关闭。
现在遇到的问题就是:对于实现导出数据的业务功能对象ExportOperate,它需要创建ExportFileApi的具体实例对象,但是它只知道ExportFileApi接口,而不知道其具体的实现,那该怎么办?
六. 实际案例:(使用模式)
1. 修改ExportOperate:
public abstract class ExportOperate { public ABC createABC(String name){ return new ABC(name,factoryMethod()); } /** * 导出文件 * @param data 需要保存的数据 * @return 是否成功导出文件 */ public boolean export(String data){ // 先完成各种导出数据前的准备工作 System.out.println("进行数据校验"); System.out.println("进行数据转换"); System.out.println("进行数据格式的封装"); // 使用工厂方法 ExportFileApi api = factoryMethod(); // 然后才真正的去导出 return api.export(data); } /** * 工厂方法,创建导出的文件对象的接口对象 * @return 导出的文件对象的接口对象 */ protected abstract ExportFileApi factoryMethod(); // 抽象类:既要约束子类的行为,又要为子类提供公共的功能 }2. 添加具体的创建器实现对象,实现创建导出成数据库备份文件形式的对象:ExportDBOperate
public class ExportDBOperate extends ExportOperate{ protected ExportFileApi factoryMethod() { // 创建导出成数据库备份文件形式的对象 return new ExportDB(); } }
3. 添加具体的创建器实现对象,实现创建导出成文本文件格式的对象:ExportTxtFileOperate
public class ExportTxtFileOperate extends ExportOperate{ protected ExportFileApi factoryMethod() { // 创建导出成文本文件格式的对象 return new ExportTxtFile(); } }
4. 客户端:client
public class Client { public static void main(String[] args) { //创建需要使用的Creator对象 ExportOperate operate = new ExportDBOperate(); //调用输出数据的功能方法 operate.export("测试数据"); } }在 ExportOperate中,因为我们并不知道究竟是创建ExportDB对象还是ExportTxtFile对象,所以我们干脆创建一个抽象的工厂方法,让子类自己去实现。
在客户端中创建的是ExportDBOperate对象,所以工厂方法创建的自然是返回ExportDB对象了。
所以工厂方法帮我们解决了在父类不知道具体实现的情况下,完成自身的功能调用,而具体的实现延迟到子类类实现。
工厂方法的实现中,通常父类会是一个抽象类,里面包含创建所需对象的抽象方法,这些抽象方法就是工厂方法。
七. Ioc和工厂方法
1. Ioc/DI对编程带来的最大变化不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在Ioc/DI思想中,应用程序就编程被动的了,被动的等待Ioc容器来创建并注入它所需要的资源了.
2. 工厂模式也是一样,我们不会主动创建ExportDB/ExportTxtFile对象,而是通过让子类实现工厂方法,让子类来创建对象。
它们的思想很类似,都是“主动变被动”,进行“主从换位”,从而获得更灵活的程序结构。小小的变动其实是编程思想的一个大进步,有效的分离了对象和它所需要的内部资源,使得阿门松散耦合,有利于功能复用。
八. 何时选用工厂方法
1. 如果一个类需要创建某个接口的对象,但是又不知道具体的实现,这种情况可以选用工厂方法模式,把创建对象的工作延迟到子类去实现。