工厂设计模式

目录

​编辑一、设计模式

二、工厂设计模式

1、什么是工厂设计模式

2、简单工厂的设计

3、通用工厂的设计

4、通用工厂的使用方式

总结


一、设计模式

什么是设计模式?

广义概念:面向对象设计,中解决特定问题的经典代码

狭义概念: GOF4人帮定义的 23 种设计模式:工厂,适配器,门面,代理,模板....


二、工厂设计模式

1、什么是工厂设计模式

什么是工厂设计模式?

通过工厂类来创建对象

原来,我们创建对象是这个样子的:

    User user = new User();
    UserDao userDao = new User();

现在,作为工厂设计模式来讲,并不提倡通过 new 来创建对象,而是通过工厂类来创建对象

这就涉及到了工厂的好处:解耦合

要想理解好解耦合这个概念,就得先了解耦合

耦合:指的是代码间的强关联关系,一方的改变会影响到另一方

我们通过一张图来理解一下:

耦合最大的问题就是不利于代码的维护

可以简单的理解为把接口的实现类,硬编码在程序中

通过工厂类,就可以实现解耦合


2、简单工厂的设计

此时,我们的代码中,有一个 UserService 对象,它是整个系统的业务类,完成核心的业务功能

在这个类中, 我们提供了两个方法:登录方法 login 和 注册方法 register

此外,我们为这个接口,定义了具体的实现类:

DAO 中的代码是这样的:

然后我们再创建一个类,用于测试工厂类解耦合的操作

然后运行程序,发现和我们预期的效果是完全一样的

那么我们现在来分析一下,现在的程序是否存在什么问题呢?

在这个程序中,在这行代码里,就出现了我们之前所说的耦合

UserService 这个实现接口 UserServiceImpl 被硬编码在了程序当中,这就是耦合

一旦出现了这个耦合之后,我们程序的维护性就非常差了,日后我们想要更换这个程序的时候,呢么我们的调用者也就是测试类也跟着要改变

比如,我们后面我们提供了一个 UserServiceImplNew 来替换掉 UserServiceImpl 这个代码,那么必然要重新更改之前的代码,如果这么进行了修改,就会面临一个编译和重新部署的问题,那么也不符合面向对象设计中的开闭原则

此时,我们就发现了, 这就是耦合

要想解决这个耦合,就要借助工厂设计模式,借助工厂类来解耦合

那么我们就来创建一个工厂类

此时,我们创建 UserSrvice 的时候,产生了耦合,所以此时我们工厂类中的工厂方法来创建 UserSrvice 就能达到解耦合的目的,那么此时我们的返回值就是 UserSrvice

由于工厂方法,只是用于创建对象,所以在这,我们可以把它修饰成 static

java 复制代码
    public static UserService getUserService() {
        
        return new UserServiceImpl();
        
    }

此时,我们就可以把之前的代码修改成直接通过工厂类创建对象

此时,站在调用者的角度,这部分的耦合就解决掉了

接下来,我们就看一看工厂类中这部分的耦合怎么解决掉

这个地方存在耦合的原因,就是因为我们在创建 UserServiceImpl 对象的时候,采用了 new 构造方法的形式来创建对象,这个方式一定会把接口的实现类硬编码在程序中,就一定会存在耦合

要想解决掉这个耦合,我们就得先回顾一个我们曾经学过的一个知识点:对象的创建方式

对象的创建一共有两种方式:

1、直接调用构造方法创建对象

java 复制代码
UserService userService = new UserServiceImpl();

2、通过反射的形式来创建对象,解耦合

java 复制代码
Class clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
UserService userService = (UserService)clazz.newInstance();

那么,我们在工厂类中,就可以运用第二种通过反射的方式来创建对象:

java 复制代码
 public static UserService getUserService() {

        UserService userService = null;
        try {
            Class clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
            userService = (UserService) clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return new UserServiceImpl();

    }

我们刚才说,反射可以解耦合,那么反射真的把耦合给解决了吗?

并没有,我们可以发现,在 forName 这个方法中,我们传入的是权限定名

如果有朝一日,我们把权限定名所对应的 UserServiceImpl 替换成新的类,那么这个权限定名也要替换成新类型的权限定名

所以光通过反射这一种方式,并不能彻底把耦合解决掉,我们还需要把耦合的字符串的信息进行一个处理

我们可以通过小的配置文件来解决这个问题:我们把这个字符串的信息,从代码当中转移到一个小的配置文件当中去,这个小的配置文件,一般用 properties 类型的文件来充当

我们可以将配置文件放在我们 resourses 的文件夹当中,那么在这个配置文件当中,我们书写的是在工厂当中,我们所发现的耦合的字符串,也就是这个类的权限定名

那么 propertices 有一个特殊的语法要求:就是得以 key-values 的形式来写

当我们把这个信息转换到小的配置文件中后,还需要将文件读回来,我们可以用一个 properties 类型的集合来存储 properties 文件的内容

那么这个 properties 类型的集合有什么特点呢?

它是一个特殊的 map,properties 中的 key 和 value 都必须得是字符串类型的,那么后续我们就可以把 properties 中的键和值最终封装在这个 properties 类型的集合当中

也就是说,这个 properties 的集合,会有一个 key :userService ,有一个 value:com.baizhiedu.basic.UserServiceImpl

那么我们就可以通过这样的方式,来进行文件内容的读取

那么我们先创建一个 properties 类型的变量:

此时里面并没有我们相关的内容,显然,下一步我们要做的事就是把文件的内容读到集合当中来,让文件内的 key ,作为集合当中的 key,让文件当中的 value 作为集合当中的 value

IO 在整个 java 操作过程中,是一个系统级资源,我们要尽量避免重复性的打开 IO ,并且最好是在程序启动的时候,一次性读取我们想要的内容,那么在这里,我们读取文件的过程中,可以使用静态代码块的方式来完成

那么作为静态代码块,它所做的工作为:

1、获得 IO 流

2、把文件的内容封装到 properties 的集合中,让 userService 作为我们集合当中的 key ,value 则为 com.baizhixx.UserServiceImpl

此时,反射中的代码也要做出相应的改变:

此时,就没有耦合了


通过刚才的工厂类,我们就把 Service 对象的耦合给解决掉了,那么现在我们再来看看我们的项目,除了 Service 对象会有耦合,其它地方是否也存在耦合现象呢?

我们来看一下 UserServiceImpl 这个类

很明显,我们在创建 UseDAO 对象的时候采用的是 new 的方式,显然这块是存在耦合的

我们把 DAO 的实现类,硬编码在了程序中,如果要解决这个实现类,显然是要用到工厂类来创建 UserDAO 的对象,此时,我们就在工厂类中提供 UserDAO 的创建方法,进而解决耦合

需要注意的是,这一块还会有我们曾经说的耦合,所以这个类名,我们最好不要写在代码当中,还是要写在配置文件里

那么工厂当中的代码,此时就应该通过 env .getProperty() 这个方法来获取

此时我们就把耦合转移到了配置文件当中

此时,UserServiceImpl 中也要进行相应的改变了

那么进而就把这个耦合给解决了


3、通用工厂的设计

前面我们解决了 UserService 和 UserDAO 的耦合问题,但是在设计层面上我们会发现一些问题,我们来看一下代码

我们会发现,如果我们想要给 UserService 解耦合,就要创建一个 getUserService 方法,想要给 UserDAO 解耦合,就要创建一个 getUserDAO 方法,那么未来在我们的工厂里面,它的工厂方法可能是无穷无尽的,只要有一个对象解耦合,我们就要提供一个对应的方法

可是此时,我们仔细观察一下,不管是 getUserDAO 还是 getUserService 它们之间有大部分的代码都是重复的

我们会发现,它们的结构和使用的代码,基本上是一样的,只有 key 是有所区别的

所以此时就会有大量的冗余代码出现

那么,我们就可以尝试着设计一个通用的工厂方法,通用的工厂里面,我们就提供一个工厂方法,可以帮我们生产一切我们想要的对象

那么我们该如何设计这个通用的工厂方法呢?

一个方法, 在通用的过程中,其实是有两个部分的:方法的声明和方法的实现

那么声明首先是我们要解决的问题,然后再去解决方法的实现

方法的声明的要素:修饰符,返回值类型,方法名,参数表,(异常)

我们会发现,它们的修饰符都是 pulic static ,所以通用的方法,修饰符也应该是 public static

那么返回值代表它所创建的对象,getUserService 返回的是 UserService ,getUserDAO 返回的是 UserDAO,那么我们现在是通用的方法,就可以让它返回 Object

方法名则没有特殊的要求,我们完全可以根据我们的需要,来做定义

参数表,我们还是按照简单的没有参数的样子来做设计


那么方法的实现怎么写呢?我们再次进行分析

那么我们继续按照套路模拟就行了

唯一的区别就是:如果是 UserService ,就得到小的配置文件中找 userService ,如果是 USerDAO,就得到小的配置文件中找 userDAO

但是在通用工厂方法中,我们并不知道想创建的是什么,那么怎么办呢?

我们可以让调用者通过参数来传给我们,那么此时,我们的代码就可以这样来写:

在参数中定义一个 key ,这个 key 指的是小的配置文件中的 userService 或者 userDAO

那么接下来,我们把这个思路,变成代码:

java 复制代码
     public static Object getBean(String key){
         Object ret = null;
         try {
             Class clazz = Class.forName(env.getProperty(key));
             ret = clazz.newInstance();
         } catch (Exception e) {
            e.printStackTrace();
         }
         return ret;
     }

4、通用工厂的使用方式

通用工厂在使用当中,应该遵从什么样的使用步骤呢?

首先,我们需要注意,通用工厂核心的目的是创建对象,而创建对象首先我们应该去定义类型,比如,我们想创建 User 类,就得先去定义 User 类型

所以第一步,就是去定义类型(类)

通过 new 去定义一个类是有耦合的,所以我们需要去通过工厂类来创建这个类型,而工厂是我们已经创建好了的,而我们想要创建的类型是我们新定义的

那我们怎么让工厂类认识我们新定义的类呢?

所以第二步就是通过配置文件的配置告知工厂(applicationContext.properties)

配置的形式也是比较简单的,就是典型的 key - value

告知工厂之后,我们需要从工厂中获得这个对象

所以第三步就是通过工厂来获得类的对象

java 复制代码
 Object ret = BeanFactory.getBean(" key ");

总结

Spring 框架的本质就是工厂,只不过这个工厂不用我们再来开发了,Spring 创建的这个工厂叫做 ApplicationContext(applicationContext.xml)

所以,作为 Spring 来讲,主要应用的就是它给我们提供的工厂以及配置文件

相关推荐
P.H. Infinity几秒前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天5 分钟前
java的threadlocal为何内存泄漏
java
caridle16 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^21 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋325 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花29 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端32 分钟前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan39 分钟前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer0843 分钟前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫