使用工厂模式解耦

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 耦合:程序间的依赖关系耦合包括:1. 类与类之间的依赖关系2. 方法与方法之间的依赖关系

耦合

耦合:程序间的依赖关系

耦合包括:

  1. 类与类之间的依赖关系
  2. 方法与方法之间的依赖关系

解耦

解耦:降低程序间的依赖关系

一、由JDBC引出解耦

先上一段熟悉的jdbc代码

       //1.导入驱动jar包

       //2.注册驱动

       DriverManager.registerDriver(newcom.mysql.jdbc.Driver());

       //3.获取数据库连接对象

       Connectionconn=DriverManager.getConnection("jdbc:mysql://localhost:3306/contest?useUnicode=true&characterEncoding=UTF-8", "user", "password");

       //4.定义SQL语句

       Stringsql="update hello set age=30 where id=1";

       //5.获取执行SQL的对象Statement

       Statementstmt=conn.createStatement();

       //6.执行SQL

       intresult=stmt.executeUpdate(sql);

       //7.处理结果

       System.out.println(result);

       //8.释放资源

       stmt.close();

       conn.close();

显然,当我们不导入mysql的驱动jar包,这段代码在编译期就会报错,因为我们用到了com.mysql.jdbc.Driver(),会导致类的独立性很差。如何解决上诉问题?

Class.forName("com.mysql.jdbc.Driver");

我们常常使用上诉代码来加载驱动,这样编译期不再依赖于某个具体的驱动类,而依赖一个字符串。查看com.mysql.jdbc.Driver类的源码发现静态代码块(当类被加载进内存,静态代码块会自动执行)

static {

       try {

           DriverManager.registerDriver(newDriver());

       } catch (SQLExceptionvar1) {

           thrownewRuntimeException("Can't register driver!");

       }

   }

可知,Class.forName("com.mysql.jdbc.Driver");本质上还是DriverManager.registerDriver(Driver driver),但这样封装一下,不仅提高了代码的独立性(不导jar包,编译期不报错,运行时会报错),而且简化了代码。

我们知道字符串在代码中是写死的,当我们想要更换为其他数据库的驱动时,还要更改代码,十分不便。如何解决上诉问题?

jdbc.properties

url=jdbc:mysql:///contest

user=xxx

password=xxx

driver=com.mysql.jdbc.Driver

我们可以通过读取配置文件来获取需要的数据

           //1.创建properties集合类

           Propertiespro=newProperties();

           //获取src目录下文件的方式--> ClassLoader 类加载器(可以加载字节码文件进内存,并且可以获取src目录下资源的路径)

           ClassLoaderclassLoader=JDBCUtils.class.getClassLoader();

           URLres=classLoader.getResource("jdbc.properties");

           Stringpath=res.getPath();

           //2.加载文件

           pro.load(newFileReader(path));

           //3.获取数据,并赋值

           url=pro.getProperty("url");

           user=pro.getProperty("user");

           password=pro.getProperty("password");

           driver=pro.getProperty("driver");

           //4.注册驱动

           Class.forName(driver);


综上操作,可以实现编译期不依赖某个类,运行时才依赖,降低了类与类之间的依赖关系,实现了解耦

解耦的思路

  1. 使用反射来创建对象,而避免使用new关键字
  2. 通过读取配置文件来获取要创建的对象的全限定类名

二、将解耦思想运用到三层架构

三层架构

因为面向接口编程,所以三层架构的每一层我们都写了一个接口,在impl包写实现类,使用的是实现类对象

步骤1:创建持久层接口

packagecn.upeveryday.dao;

/**

* 账户的持久层接口

*/

publicinterfaceIAccountDao {

   voidsaveAccount();

}

步骤2:创建持久层实现类

packagecn.upeveryday.dao.impl;

importcn.upeveryday.dao.IAccountDao;

/**

* 账户的持久层实现类

*/

publicclassAccountDaoImplimplementsIAccountDao {

   publicvoidsaveAccount() {

       System.out.println("保存成功!");

   }

}

步骤3:创建业务层接口

packagecn.upeveryday.service;

/**

* 账户的业务层接口

*/

publicinterfaceIAccountService {

   /**

    * 模拟保存

    */

   voidsaveAccount();

}

步骤4:创建业务层实现类

packagecn.upeveryday.service.impl;

importcn.upeveryday.dao.IAccountDao;

importcn.upeveryday.service.IAccountService;

/**

* 账户的业务层实现类

*/

publicclassAccountServiceImplimplementsIAccountService {

   //业务层调用持久层

   privateIAccountDaoaccountDao=newAccountDaoImpl();

   publicvoidsaveAccount() {

       accountDao.saveAccount();

   }

}

步骤5:创建表现层

packagecn.upeveryday.ui;

importcn.upeveryday.factory.BeanFactory01;

importcn.upeveryday.factory.BeanFactory02;

importcn.upeveryday.service.IAccountService;

/**

* 模拟一个表现层,用于调用业务层

*/

publicclassClient {

   publicstaticvoidmain(String[] args) {

       AccountServiceImplservice=newAccountServiceImpl();

       service.saveAccount();

   }

}

三层架构解耦分析

我们知道,在三层架构中业务层调用持久层,表现层调用业务层

由解耦思路:

  1. 使用反射来创建对象,而避免使用new关键字
  2. 通过读取配置文件来获取要创建的对象的全限定类名

可以使用工厂模式解耦

三、工厂模式解耦

/**

* 一个创建Bean对象的工厂

* Bean:在计算机英语中,是可重用组件的意思

*      可重用组件:业务层、持久层都是可重用组件(表现层调用业务层,业务层调用持久层)

* javabean:用java语言编写的可重用组件

*      javabean范围 > 实体类范围

* BeanFactory就是用来创建service和dao对象的:

* 1.需要一个配置文件来配置我们的service和dao(配置文件可以是xml或properties)

*      配置的内容:唯一标识符=全限定类名(key=value)

* 2.读取配置文件内容,反射创建对象

*      配置文件可以是xml,也可以是properties

*/

单例:从始至终只有一个实例对象。例如:servlet

多例:有多个对象

如果是多例的,对象会被创建多次,类中的成员变量每次都会被初始化,因此不存在线程安全问题

单例,对象只被创建一次,类中的成员变量也只被初始化一次,则会产生线程安全问题

对象被创建多次,执行效率没有单例对象高。而且我们在service和dao中并没有定义成员变量,因此不存在线程安全问题

工厂模式实现了由自己控制资源转变为工厂factory控制资源,从而实现控制反转IOC(Inversion of Control),解决了程序间的依赖关系,spring是使用配置的方式实现工厂模式进而实现解耦,而不需要我们自己编写beanFactory代码

IOC只能解决程序间的依赖关系


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
前端开发
Vite——如何配置使用sass
Vite——如何配置使用sass
786 0
|
存储 Java 测试技术
Java课程在线视频学习系统的设计与实现
Java课程在线视频学习系统的设计与实现
481 0
|
BI
解决File.delete()删除不掉文件
首先注意两点: 此文件被使用的时候无法删除(比如网络输出没关闭流) 判断此文件是否存在再做删除(exists) 删除文件夹之前先删除文件夹下的所有文件(递归解决) 判断是否删除成功会有返回值,文件名错了的话,删除文件不会报错。
2438 0
IDEA常用配置之代码自动格式化删除无用导入
IDEA常用配置之代码自动格式化删除无用导入
736 1
idea+git实现提交自动去除无用import以及格式化代码
idea+git实现提交自动去除无用import以及格式化代码
743 0
|
SQL Java 数据库连接
MyBatis 常见错误
为初学者准备的常见MyBatis 异常汇总 ,持续完善中
2901 0
|
JavaScript 前端开发
VSCode .vue 文件 html css 无智能提示
VSCode .vue 文件 html css 无智能提示
1428 0
|
Web App开发 网络协议 Java
|
监控 网络协议 网络安全
计算机网络实验——华为eNSP模拟器常用命令总结(总结的非常详细( •̀ .̫ •́ )✧快来看啊)
计算机网络实验——华为eNSP模拟器常用命令总结(总结的非常详细( •̀ .̫ •́ )✧快来看啊)
计算机网络实验——华为eNSP模拟器常用命令总结(总结的非常详细( •̀ .̫ •́ )✧快来看啊)