设计模式-简单工厂模式

224 阅读3分钟

定义:

由一个工厂对象决定创建出哪一种类型的产品。

类型

创建型,但是不属与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图

    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
  • jdk - Class中的forName

    • 例如在在使用jdbc的第一步Class.forName("xxxDriver")来动态加载驱动,Class.forName就是使用的简单工厂模式,类似于上述我们自己写的利用Class反射的例子
    • 扩展:第一步加载驱动之后,调用DriverManager的registerDriver方法注册Driver,第二部调用Driver的getConnection方法获取与数据库的链接。(注册与获取Driver均源自于下图的成员变量)

  • logback的LogFactory中的getLogger方法
    • 通过简单工厂去创建日志对象,LogFactory中的getLogger方法利用ILoggerFactory去实例化日志对象,下面的截图是一个ILoggerFactory的实现类的实例化代码片段,使用了简单工厂模式