工厂方法模式顾名思义即把工厂模式在一个方法中实现,也就是定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到其子类上。
现在有这样一种情况,对于实现导出数据的业务功能对象,我们需要创建导出数据接口的具体实现对象,但是只知道这一个接口,而不知道具体的实现,那该怎么办?
分析上述问题,事实上在实现导出数据的业务功能对象里面,根本不知道究竟要使用哪一种导出文件的格式,因此这个对象根本就不应该和具体的导出文件的对象耦合在一起,它只需要面向导出对象的接口就可以了。
那么,问题又来了,接口是不能直接使用的,需要使用具体的接口实现对象的实例。这不是自行矛盾吗?要求面向接口,不让和具体的实现耦合,但又需要创建接口的具体实现对象的实例。怎么解决这个矛盾那?
工厂方法模式的解决思路很有意思,那就是不解决,采用无为而治的方法,不是需要接口对象吗,那就定义一个方法来创建,可事实上它自己是不知道如何创建这个借口对象的,没关系,定义成抽象方法就可以了,自己实现不了,那就让子类来实现,这样这个对象本身就可以只是面向接口编程,而无需关心到底如何创建接口对象了。那么,针对上述问题,使用工厂方法模式来实现示例的程序结构为:
1、先把导出文件对象的接口定义出来
public interface ExportFileApi {
/**
* 导出内容为文件
* @param data 需要保存的数据
* @return 是否导出成功
*/
public boolean export(String data);
}
2、实现接口ExportFileApi,为了代码简单,只实现了导出文本文件和数据库备份文件,并且我在实现部分只是简单的示意一下,代表这里需要操作的文件。
(1)实现导出文本文件格式
public class ExportTextFile implements ExportFileApi {
@Override
public boolean export(String data) {
System.out.println("导出数据"+ data + "到文本文件中");
return true;
}
}
(2)实现导出数据库备份文件形式对象
public class ExportDB implements ExportFileApi {
@Override
public boolean export(String data) {
System.out.println("导出数据"+ data + "到数据库备份文件中");
return true;
}
}
3、实现ExportOperate的示例代码:
public abstract class ExportOperate {
/**
* 导出文件
* @param data 需要保存的对象
* @return 是否成功的导出文件
*/
public boolean export(String data) {
ExportFileApi api = factoryMethod();
return api.export(data);
}
/**
* 工厂方法,创建导出的文件对象的接口对象
* @return 导出的文件对象的接口对象
*/
protected abstract ExportFileApi factoryMethod();
}
4、加入两个ExportOperate的子类实现:
(1)创建导出成文本文件格式对象的示例代码:
public class ExportTextFileOperate extends ExportOperate {
@Override
protected ExportFileApi factoryMethod() {
return new ExportTextFile();
}
}
(2)创建导出成数据库备份文件形式对象的示例代码:
public class ExportDBOperate extends ExportOperate {
@Override
protected ExportFileApi factoryMethod() {
return new ExportDB();
}
}
5、客户端直接创建需要使用的接口实现对象,然后调用相应的功能方法
public class Client {
public static void main(String[] args) {
ExportOperate operate = new ExportDBOperate();
operate.export("【测试数据】");
}
}
运行结果如下:
导出数据【测试数据】到数据库备份文件中
工厂方法模式的主要功能是让父类在不知道具体实现的情况下,完成自身功能的调用;而具体的实现延迟到子类上来实现。这样在设计的时候,不用去考虑具体的实现,需要某个对象时,把它通过工厂方法返回就好了,在使用这些对象实现功能的时候还是通过接口来操作,这类似于IOC/DI的思想,这个思想在IOC/DI这篇博客中有介绍。
当然,我们也可以像简单工厂一样,把工厂方法参数化,即通过给工厂方法传递参数,让工厂方法根据参数的不同来创建不同的产品对象。那样的话,改造上述代码示例(在这里没有变化的类不在重复):
主要会对ExportOperate类进行修改,该类将不再是抽象类了,切工厂方法也不再是抽象的了,该方法需要根据参数来选择实现的方式,其代码示例如下:
public class ExportOperate {
/**
* 导出数据
* @param type 用户选择导出的类型
* @param data 需要保存的数据
* @return 是否成功的导出文件
*/
public boolean export(int type, String data) {
ExportFileApi api = factoryMethod(type);
return api.export(data);
}
/**
* 工厂方法,创建导出的文件对象的接口对象
* @param type 用户选择的导出类型
* @return 导出文件对象的接口对象
*/
protected ExportFileApi factoryMethod(int type) {
ExportFileApi api = null;
if(type == 1) {
api = new ExportTextFile();
} else if(type == 2) {
api = new ExportDB();
}
return api;
}
}
使用参数化工厂方法,扩展起来会非常的容易,例如我需要扩张一个导出成xml文件的代码示例如下:
public class ExportXml implements ExportFileApi {
@Override
public boolean export(String data) {
System.out.println("导出数据" + data + "到XML文件");
return true;
}
}
然后扩展ExportOperate类,来加入新的实现,示例代码如下:
public class ExportOperate3 extends ExportOperate2 {
/**
* 覆盖父类中的工厂方法
* @param type 用户选择的导出类型
* @return 导出的文件对象的接口对象
*/
@Override
protected ExportFileApi factoryMethod(int type) {
ExportFileApi api = null;
if(type == 3) {
api = new ExportXml();
} else {
api = super.factoryMethod(type);
}
return api;
}
}
在这里,大家会发现工厂方法模式的本质就是延迟到子类来选择实现,这也是与简单工厂模式不同之处(简单工厂模式是直接在工厂类中进行选择实现),它具有可以在不知道具体实际情况下编程和更容易扩展对象的新版本等优点。