1.1 程序中耦合、概念理解
- 在软件工程中,耦合指的就是对象之间的关联性。对象之间耦合性越高,维护成本就越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则是高内聚、低耦合。
- 高内聚、低耦合:简单来说,就是类内部的关系越紧密越好。类与类之间的关系越少越好。
1.2 程序中耦合
1.2.1 代码体现
/**
* 耦合:
* 指的是程序中的依赖关系。
* 依赖关系还有很多种分类。
* 现在看到的是类的依赖。
* 解决依赖关系:
* 使用反射注册驱动
* 使用反射创建对象产生的问题:
* 驱动类字符串在代码中写死了,如果遇到修改,又要改源码。
* 解决写死了字符串的问题:
* 使用配置文件
*
* 在项目开发中,我们应该做到:
* 编译器不依赖,运行时才依赖
*/
public class JdbcDemo1 {
大家思考:如何解决下面的连接数据库代码与数据库驱动代码耦合问题?
public static void main(String[] args) throws Exception{
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisdb", "root", "root");
//3.获取操作数据库的预处理对象
PreparedStatement pstm = conn.prepareStatement("select * from account");
//4.执行SQL语句
ResultSet rs = pstm.executeQuery();
//5.遍历结果集
while(rs.next()) {
System.out.println(rs.getString("name"));
}
//6.释放资源
rs.close();
pstm.close();
conn.close();
}
}
1.2.2 解决思路
- 在jdbc中,是这样注册驱动的,代码如下:Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串。好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了,没有驱动不可能运行成功的) 。同时,也产生了一个新的问题, mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。解决这个问题也很简单,使用配置文件配置。
1.2.3 思考
- 在开发中的三层架构分别是dao、service、controller, 如何降低这三层的耦合度?
- 实现的目标: 修改dao的实现,不用修改service层,实现service与dao的解耦。
1.3 实现步骤
1.3.1 准备
dao接口
public interface IAccountDao {
void save();
}
实现
public class AccountDaoImpl implements IAccountDao {
@Override
public void save() {
System.out.println("保存账户!");
}
}
public class AccountDaoOracleImpl implements IAccountDao {
@Override
public void save() {
System.out.println("Oracle保存账户!");
}
}
service接口
public interface IAccountService {
void save();
}
实现
public class AccountServiceImpl implements IAccountService {
// 创建dao
private IAccountDao accountDao = new AccountDaoImpl();
@Override
public void save() {
accountDao.save();
}
}
1.3.2 工厂解耦
定义properties配置文件
# dao
# default database (mysql)
#accountDao =com.ftjf.dao.impl.AccountDaoImpl
# oracle database
accountDao = com.ftjf.dao.impl.AccountDaoOracleImpl
# service
account
定义工厂
/**
* 创建对象的工厂,主要加载配置文件创建dao、service对象
*/
public class BeanFactory {
/**
* 根据指定的key,读取配置文件创建对象
* @return
*/
public static <T> T getBean(String key,Class<T> clazz){
try {
// 加载Properties配置文件
// 通过ResourceBundle加载配置文件:
// 1. 只可以加载properties后缀的配置文件
// 2. 只能加载类路径下的properties配置文件
ResourceBundle bundle = ResourceBundle.getBundle("instance");
// 根据key获取value:com.htjf.dao.impl.AccountDaoImpl
String value = bundle.getString(key);
// 创建对象返回
return (T)Class.forName(value).getConstructor().newInstance();
} catch (Exception e) {
// 异常转型,调用者可以处理可以不处理(灵活)
throw new RuntimeException(e);
}
}
}
修改service
public class AccountServiceImpl implements IAccountService {
// 创建dao
//private IAccountDao accountDao = new AccountDaoImpl();
// 新需求:更换了数据库改为oracle
//private IAccountDao accountDao = new AccountDaoOracleImpl();
// 分析:现在更改了数据库,dao重新写了实现,对sevice有影响,要修改代码。
// 期望:改dao,不修改service代码。(service与dao解耦的设计)
// 通过工厂创建对象
private IAccountDao accountDao = BeanFactory.getBean("accountDao",IAccountDao.class);
@Override
public void save() {
accountDao.save();
}
}
1.4 Inversion Of Control(IOC)控制反转
前面我们使用了工厂模式,实现了表现层-业务层-持久层的解耦。
它的核心思想是:
1.通过读取配置文件反射创建对象。
2.把创建出来的对象都存起来,当我们下次需要使用的时候可以直接从存储位置获取。什么是控制反转?把对象的创建交给外部的容器,就是控制反转。