使用工厂模式解耦

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 耦合:程序间的依赖关系耦合包括: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只能解决程序间的依赖关系


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
设计模式 算法 C++
设计模式之门面模式(C++)
设计模式之门面模式(C++)
|
6月前
|
设计模式
**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合
【6月更文挑战第23天】**工厂模式与抽象工厂模式**都是创建型设计模式,用于封装对象创建,减少耦合。工厂模式专注于单个对象,通过具体工厂创建具体产品,适用于简单对象创建;抽象工厂则关注一系列相关产品,提供创建一族对象的接口,适用于处理多个不兼容产品族。选择模式基于问题域的复杂性,单个产品需求时用工厂模式,多产品族时用抽象工厂模式。
33 5
|
2月前
|
设计模式 网络协议 Java
03.建造者模式设计思想
本文详细介绍了建造者模式的设计思想及其应用场景。主要内容包括建造者模式的由来、定义、适用场景及思考,通过实例讲解了如何使用建造者模式解决复杂对象的创建问题。文章还对比了建造者模式与工厂模式的区别,并分析了建造者模式的优缺点。最后,提供了多个相关资源链接,帮助读者深入理解和应用设计模式。
25 1
|
2月前
|
设计模式 网络协议 Java
02.工厂模式设计思想
本文档详细介绍了工厂模式设计思想,包括简单工厂、工厂方法和抽象工厂三种类型。简单工厂通过参数返回不同类的实例,适合对象较少的场景;工厂方法通过定义抽象工厂接口,由子类实现具体对象的创建,增强扩展性;抽象工厂则能创建多个产品族,适用于产品间有依赖关系的情况。文档还通过咖啡店点餐系统案例,展示了三种模式的应用及优缺点,帮助读者理解和选择合适的工厂模式。
21 1
|
4月前
|
设计模式 算法 开发者
深入理解工厂模式与策略模式:设计模式的灵活应用
深入理解工厂模式与策略模式:设计模式的灵活应用
|
5月前
|
设计模式 C++
C++一分钟之-设计模式:工厂模式与抽象工厂
【7月更文挑战第14天】设计模式是解决软件设计问题的通用方案。工厂模式与抽象工厂模式是创建型模式,用于对象创建而不暴露创建逻辑。工厂模式推迟实例化到子类,但过度使用会增加复杂性。抽象工厂则创建相关对象族,但过度抽象可能造成不必要的复杂度。两者均应按需使用,确保设计灵活性。代码示例展示了C++中如何实现这两种模式。
47 3
|
7月前
|
设计模式 传感器
将设计模式门面模式运用到生活当中
将设计模式门面模式运用到生活当中
|
设计模式
设计模式~门面模式-05
门面模式 优点 缺点 使用场景
48 0
|
设计模式 Java 中间件
设计模式-结构型模式:外观模式
设计模式-结构型模式:外观模式
108 0
|
机器学习/深度学习 设计模式 缓存
设计模式——单一职责模式之装饰模式
在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
63 0