简单工厂模式负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护 。虽然最后我们改进了简单工厂模式,即使增加产品类也不需要再去修改工厂类,但是这也增加了客户端代码的复杂度(客户端需要知道创建哪个类的实例)。
工厂方法模式
工厂方法模式定义一个用于创建对象的接口,让子类去决定实例化哪一个类,工厂方法模式使一个类的实例化延迟到其子类。
下面考虑一个具体的应用场景:
实现一个导出数据的应用框架,来让客户选择数据的导出方式(如导出为json文件,xml文件),并真正的执行数据的导出。
通常情况下,在不使用任何设计模式的情况下,代码通常如下:
public class NonFactory {
public static void main(String[] args) {
ExportOperator operator = new ExportOperator();
operator.export(1, "gaga");
operator.export(2, "hahah");
operator.export(3, "dadaa");
}
}
interface DataExportApi {
public void export(String data);
}
class DataExportJson implements DataExportApi {
public void export(String data) {
System.out.println("导出数据到json文件");
}
}
class DataExportXml implements DataExportApi {
public void export(String data) {
System.out.println("导出数据到xml文件");
}
}
class ExportOperator {
DataExportApi api = null;
public void export(int type, String data) {
if (type == 1) {
api = new DataExportJson();
} else if (type == 2) {
api = new DataExportXml();
}
api.export("data");
}
}
乍一看,这个代码也没问题啊,也能实现需要的功能。但是,万一客户需要增加新的导出格式,那我们就需要修改ExportOperator类了(违反了修改关闭原则),并且这种传参数决定创建哪个子类的方式也存在很大的隐患,你永远无法预料客户给你传递的参数是什么样的,比如说咱们测试代码里边传递的参数: operator.export(3, "dadaa");就是不合法的,可能会造成程序无法正常工作。那么如何解决这个问题呢???答案是使用工厂方法模式,将实例的创建交给子类,代码如下所示:
public class Factory {
public static void main(String[] args) {
Export_Operator operator = new ExportDataXmlOperator();
operator.export("hahah");
operator = new ExportDataJsonOperator();
operator.export("");
}
}
abstract class Export_Operator {
public void export(String data) {
DataExport_Api api = factoryMethod();
api.export(data);
}
public abstract DataExport_Api factoryMethod();
}
abstract class DataExport_Api {
public abstract void export(String data);
}
class ExportDataXmlOperator extends Export_Operator {
public DataExport_Api factoryMethod() {
return new ExportDataXml();
}
}
class ExportDataJsonOperator extends Export_Operator {
public DataExport_Api factoryMethod() {
return new ExportDataJson();
}
}
class ExportDataXml extends DataExport_Api {
public void export(String data) {
System.out.println("导出到xml文件");
}
}
class ExportDataJson extends DataExport_Api {
public void export(String data) {
System.out.println("导出到json文件");
}
}
其类图结构如下所示:
这样一来的话,当我们需要增加新的数据导出格式的时候,只需要增加一个DataExport_Api的子类以及对应的Export_Operator 的子类即可,实现了对修改关闭。但是随着子类的增加,工厂类也增加,类的数量会越来越多,造成类膨胀。其实,对简单工厂的改进办法也同样可以用来改进工厂方法模式,下面是改进的代码:
public class Factory {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Export_Operator operator = new ExportDataOperator();
operator.export("hahah", ExportDataXml.class);
operator.export("", ExportDataJson.class);
}
}
abstract class Export_Operator {
public void export(String data, Class<? extends DataExport_Api> clzz) throws InstantiationException, IllegalAccessException {
DataExport_Api api = factoryMethod(clzz);
api.export(data);
}
public abstract DataExport_Api factoryMethod(Class<? extends DataExport_Api> clzz) throws IllegalAccessException, InstantiationException;
}
abstract class DataExport_Api {
public abstract void export(String data);
}
class ExportDataOperator extends Export_Operator {
public DataExport_Api factoryMethod(Class<? extends DataExport_Api> clzz) throws IllegalAccessException, InstantiationException {
return clzz.newInstance();
}
}
class ExportDataXml extends DataExport_Api {
public void export(String data) {
System.out.println("导出到xml文件");
}
}
class ExportDataJson extends DataExport_Api {
public void export(String data) {
System.out.println("导出到json文件");
}
}
工厂方法模式的本质是:延迟到子类来选择实现
和简单工厂一样,上述改进也不可避免的会造成调用复杂度的增加,但是却换来了工厂类的减少。具体使用过程中可以衡量对比一下再做取舍。
工厂方法模式使用场景
工厂方法模式new一个对象的替代品,所以在需要生成对象的地方都可以使用,但是需要考虑是否有必要增加一个工厂类来增加代码的复杂度。
其次,需要灵活的可扩展的框架时,可以考虑采用工厂模式方法。例如实现一个连接邮件服务器的框架(可供选择的网络协议有:POP3,IMAP,HTTP),可能需要扩展一个webservice接口等。
工厂模式的优点
1. 良好的封装性,代码结构清晰。如果一个调用者需要一个具体的产品对象,只需要知道这个产品的工厂是谁就行了,不用知道创建对象的具体过程。
2.扩展性好。在增加产品类的情况下,只需要扩展一个工厂类就可以“拥抱变化”。
3.屏蔽产品类。这一点很重要,产品类的具体实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口不变,系统中的上层模块就不需要发生变化。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品类生成是由工厂类决定的。
4.工厂模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不需要关心。