简单工厂的不足
上一节讲的简单工厂能够满足少量类的创建,但是严重破坏了开闭原则,每次添加一个新的类都得到大的工厂中去注册,然后用户才能从工厂里取出产品,更主要的是,用户必须要记住产品的名称,或者产品的类类型,这对使用者来说是不友好的。我们希望用户知道他想要的产品是在哪个工厂里,通过这个工厂就能获取他想要的,而不关心具体的创建细节,以及产品究竟放在哪。这个时候就有必要使用简单工厂的升级版—工厂方法模式
工厂方法定义
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。工厂方法模式定义了一个创建对象的接口,但由子类决定实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
定义中提到几个重要的地方:
- 子类决定创建的对象
- 实例化推迟到子类
子类决定创建的对象也就是说:之前的简单工厂是一个大的工厂,各种创建对象的活动都在这一个工厂里完成,需要用户传入类型,然后根据类型进行创建。而现在,我将这个搞那个厂定义为抽象的,里面有个创建产品的方法,继承我这个工厂的方法必须实现创建产品的方法,将创建的任务分给自己下属的各个工厂
// 原来的工厂
public class VideoFactory {
public static Video getVideo(String type) {
if ("java".equalsIgnoreCase(type)) {
return new JavaVideo();
} else if ("python".equalsIgnoreCase(type)) {
return new PythonVideo();
} else {
return null;
}
}
}
// 现在的工厂
public abstract class VideoFactory {
public abstract Video getVideo();
}
你只要继承自我这个工厂,就得实现我这个创建的方法。
实例化推迟到子类:这个也很好理解,原来的调用方法是这样 VideoFactory.getVideo("java");
,也就是说,这个时候类就已经实例化好了,但是,如果是工厂方法,那么调用方法就是如下
VideoFactory videoFactory = new JavaVideoFactory();
Video javaVideo = videoFactory.getVideo();
JavaVideoFactory.java
方法如下
public class JavaVideoFactory extends VideoFactory {
public Video getVideo() {
return new JavaVideo();
}
}
在创建具体的工厂时不必创建子类,只有我真正去取的时候才会进行创建,也就是将实例化推迟到子类
抽象类还是接口
在创建 VideoFactory
类的时候,是将它定义为抽象类还是定义为接口呢?我认为应该具体抉择:如果你的目的很单纯,就是一个产品一个工厂,且职责就是创建产品,没有其他的功能,那么抽象类和接口都是 OK 的,不管用接口还是抽象类引用都是无所谓的。但是如果你还想有一些默认方法或者一些默认动作,比如子类工厂都有的公共部分,可以抽取出来,这个时候就可以使用抽象类提供的默认方法等,只把不同的地方即创建产品进行抽象即可。所以还得看具体的问题分析
工厂方法模式的优缺点
由于每次增加新的产品都得创建对应的工厂,这就很容易导致类的数量过多,增加系统的复杂度,同时可能需要使用 DOM、反射等技术实现,这就增加了理解的难度。
但是工厂方法也是有不错的有点的,首先,用户不需要知道产品名称是什么,只需要产品在哪个工厂,通过工厂创建即可,其次,简单工厂符合开闭原则,增加新的产品时,只要增加一个产品类和一个具体的工厂类即可,不需要修改已有的代码,可扩展性非常的强,最后由子类决定创建何种对象,将任务分配到各个子类中去,维护起来更家的容易。
所以说,工厂方法应用的地方就是他的优点适用的地方
工厂方法在 JDK 中的应用
Collection
接口中的iterator()
方法就是一个工厂方法,只要实现了这个接口的类都得实现这个方法- JDBC 中的数据库连接,切换数据库只要更改连接驱动即可
等等,工厂方法模式的应用还是挺广的,下一节将会对抽象工厂方法具体的分析