使用工厂模式解耦

本文涉及的产品
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 
目录
相关文章
|
存储 Java Nacos
Spring Cloud+Nacos+KMS 动态配置最佳实践
本文讲述了 Spring Cloud 应用中结合 Nacos 实现了运行期配置动态更新的功能,以及在此基础上结合 KMS 在不改动代码的情况下对应用使用的敏感配置进行保护,解决将配置迁移到 Nacos 中可能存在的数据安全顾虑,并对其底层工作原理做了简单介绍。
1397 153
|
存储 算法 安全
中间件数据传输密钥生成
中间件数据传输密钥生成是一个复杂且关键的过程,需要综合考虑安全性、性能和合规性等因素。在实际应用中,建议根据具体需求和场景选择合适的算法和工具来实现密钥生成和管理。
219 1
|
关系型数据库 Java 数据库
使用Kettle动态生成页码并实现分页数据同步
使用Kettle动态生成页码并实现分页数据同步
1022 0
使用Kettle动态生成页码并实现分页数据同步
|
存储 缓存 监控
Win硬件 - 西部数据绿盘、蓝盘、黑盘、红盘和紫盘有什么区别?
Win硬件 - 西部数据绿盘、蓝盘、黑盘、红盘和紫盘有什么区别?
899 0
|
SQL Oracle 关系型数据库
|
2天前
|
云安全 人工智能 安全
AI被攻击怎么办?
阿里云提供 AI 全栈安全能力,其中对网络攻击的主动识别、智能阻断与快速响应构成其核心防线,依托原生安全防护为客户筑牢免疫屏障。
|
12天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~
|
6天前
|
安全 Java Android开发
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
崩溃堆栈全是 a.b.c?Native 错误查不到行号?本文详解 Android 崩溃采集全链路原理,教你如何把“天书”变“说明书”。RUM SDK 已支持一键接入。
474 199