定义:
由一个工厂对象决定创建出哪一种类型的产品。
类型
创建型,但是不属与23种设计模式。
使用场景
- 工厂类负责创建的对象比较少
- 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(创建对象的逻辑)并不关心
优点
- 应用层只需要传入一个正确的参数,就可以获取到所需要的对象,无需关注其创建细节。
缺点
- 工厂类的职责过重,新增新的产品需要修改工厂类的判断逻辑,违背开闭原则。(单点故障)
代码demo
描述xxxxxxxxxxxxxxxxxxxx
先来看一种不适用简单工厂的使用方法
课程抽象类
public abstract class Video {
/**
* 生产视频
*/
public abstract void prod();
}
JAVA课程类
public class JavaVideo extends Video{
public void prod() {
System.out.println("这是java视频");
}
}
Python课程类
public class PythonVideo extends Video{
public void prod() {
System.out.println("这是python视频");
}
}
测试类
public class Test {
public static void main(String[] args) {
Video video = new PythonVideo();
video.prod();
}
}
测试类执行结果:输出 这是Python视频
-
UML图
-
以上写法也能实现需求,不过高层模块/应用模块(这里指的是Test.java)需要依赖于低层模块(这里指的是PythonVideo.class),违背依赖倒置原则。如果此时需要JAVA课程那么需要再声明一个JAVA课程类,如果是在不同模块同时还需要导包,不够灵活。
简单工厂改造之后的写法
创建工厂类
public class VideoFactory {
public Video getVideo(String type){
if ("java".equalsIgnoreCase(type)){
return new JavaVideo();
}else if ("python".equalsIgnoreCase(type)){
return new PythonVideo();
}else {
return null;
}
}
}
改造测试类
public class Test {
//使用简单工厂来改造生产产品
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo("java");
if (video != null){
video.prod();
}
}
}
- UML图
- 利用VideoFactory工厂来创建课程,应用层只需要传入相应的参数,工厂就会自动创建所需要的产品,但是这样的简单工厂会存在这文章前面描述的问题,若此时新增一个vue课程,那么需要修改工厂类的代码,修改创建逻辑。下面我们利用反射来弥补这个不足。
利用反射完善简单工厂
改造后的工厂类
public class VideoFactory {
public Video getVideo(Class clazz) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Video video = null;
video = (Video) Class.forName(clazz.getName()).newInstance();
return video;
}
}
改造后的测试类
public class Test {
//利用反射来弥补简单工厂的扩展性
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo(JavaVideo.class);
if (video != null){
video.prod();
}
}
}
- UML图
- 根据反射来动态创建课程,即使增加vue课程,只需要新增课程实现类,生产vue课程只需要给工厂传入vue课程的class参数即可。
源码案例
-
jdk - Calendar中的createCalendar方法
- 查看源码会发现,创建Calendar在Calendar中的build()方法
- UML
- 查看源码会发现,创建Calendar在Calendar中的build()方法
-
jdk - Class中的forName
- 例如在在使用jdbc的第一步Class.forName("xxxDriver")来动态加载驱动,Class.forName就是使用的简单工厂模式,类似于上述我们自己写的利用Class反射的例子
- 扩展:第一步加载驱动之后,调用DriverManager的registerDriver方法注册Driver,第二部调用Driver的getConnection方法获取与数据库的链接。(注册与获取Driver均源自于下图的成员变量)
- logback的LogFactory中的getLogger方法
- 通过简单工厂去创建日志对象,LogFactory中的getLogger方法利用ILoggerFactory去实例化日志对象,下面的截图是一个ILoggerFactory的实现类的实例化代码片段,使用了简单工厂模式
- 通过简单工厂去创建日志对象,LogFactory中的getLogger方法利用ILoggerFactory去实例化日志对象,下面的截图是一个ILoggerFactory的实现类的实例化代码片段,使用了简单工厂模式