2. 程序的耦合及解耦
2.1 曾经案例中的问题
在具体案例中研究耦合:
2.1.1 创建 day01_eesy_01jdbc项目
2.1.2 在pom.xml中完成有关mysql的配置
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>day01_eesy_01jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.5</version>
</dependency>
</dependencies>
</project>
说明:我的电脑的MySQL数据库是5.5.40版本,配置(或导入)的mysql jar包的版本不能高于5.5.40,否则会出现连接数据库异常的现象
2.1.3 在JdbcDemo1.java类中进行操作
JdbcDemo1.java
import java.sql.*;
//程序的耦合案例
/**
* 耦合:程序间的依赖关系
* 包括:
* 1. 类之间的依赖
* 2. 方法间的依赖
*
* 解耦:
* 降低程序间的依赖关系
* 实际开发中,应该做到,编译期不依赖,运行时才依赖
* 解耦的思路:
* 第一步:使用反射来创建对象,而避免使用new关键字
* 第二步:通过读取配置文件来获取要创建的对象全限定类名
*/
public class JdbcDemo1 {
public static void main(String[] args) throws Exception{
//1.注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
// Connection connection=DriverManager.getConnection("jdbc:mysql://rm-bp1s8wez9j4hw42oemo.mysql.rds.aliyuncs.com:3306/dizi-01","dizi","Dizi1234");
Connection connection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/eesy","root","123456");
//3.获取操作数据库的预处理对象
//PreparedStatement statement=connection.prepareStatement("select * from yangzhaohui_user");
PreparedStatement statement=connection.prepareStatement("select * from account");
//4.执行SQL得到结果集
ResultSet resultSet=statement.executeQuery();
//5.遍历结果集
while (resultSet.next()){
//System.out.println(resultSet.getString("username"));
System.out.println(resultSet.getString("name"));
}
//6.释放资源
resultSet.close();
statement.close();
connection.close();
}
}
2.1.4 目录结构
2.1.5 分析
耦合:程序间的依赖关系
包括:
- 类之间的依赖
- 方法间的依赖
解耦:
降低程序间的依赖关系
实际开发中:
应该做到,编译期不依赖,运行时才依赖
解耦的思路:
第一步:使用反射来创建对象,而避免使用new关键字
第二步:通过读取配置文件来获取要创建的对象全限定类名
2.2 工厂模式解耦
2.2.1 初始创建day01_eesy_01factory工程
-
业务层接口IAccountService.java
//账户的业务层接口 public interface IAccountService { //模拟保存账户 void saveAccount(); }
-
业务层实现类AccountServiceImpl.java , 调用持久层
//账户的业务层实现类 public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao = new AccountDaoImpl(); public void saveAccount() { accountDao.saveAccount(); } }
-
持久层接口IAccountDao.java
//账户的持久层接口 public interface IAccountDao { //模拟保存账户 void saveAccount(); }
-
持久层实现类AccountDaoImpl.java
//账户的持久层实现类 public class AccountDaoImpl implements IAccountDao { public void saveAccount() { System.out.println("保存了账户"); } }
-
模拟表现层 Cilent.java , 调用业务层
//模拟一个表现层,用于调用业务层 public class Client { public static void main(String[] args) { IAccountService accountService = new AccountServiceImpl(); accountService.saveAccount(); } }
-
目录结构
初步分析: 存在类之间的依赖,每次程序显示都需要通过 new 来创建新的对象, 耦合度比较高
2.2.2 使用工厂模式对工程进行初步改进
- 改进思路
-
需要一个创建Bean对象的工厂
Bean: 在计算机英语中,有可重用组件的含义
JavaBean:用Java语言编写的可重用组件
JavaBean > 实体类它就是创建我们的service和dao对象的
-
第一个: 需要一个配置文件来配置我们的service和dao
配置的内容: 唯一标识=全限定类名(key = value) -
第二个: 通过读取配置文件中配置的内容, 反射创建对象
-
配置文件可以是xml也可以是properties
-
创建配置文件 bean.properties
accountService=com.itheima.service.impl.AccountServiceImpl accountDao=com.itheima.dao.impl.AccountDaoImpl
-
创建工厂类 BeanFactory.java
** * 一个创建Bean对象的工厂 * * Bean: 在计算机英语中,有可重用组件的含义 * * JavaBean:用Java语言编写的可重用组件 * JavaBean > 实体类 * * 它就是创建我们的service和dao对象的 * * 第一个: 需要一个配置文件来配置我们的service和dao * 配置的内容: 唯一标识=全限定类名(key = value) * 第二个: 通过读取配置文件中配置的内容, 反射创建对象 * * 配置文件可以是xml也可以是properties */ public class BeanFactory { //定义一个Properties对象 private static Properties properties; //定义一个Map,用于存放我们要创建的对象,我们把它称之为容器 private static Map<String,Object> beans; //使用静态代码块为Properties对象赋值 static { try { //实例化对象 properties=new Properties(); //获取properties文件的流对象 InputStream in= BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); properties.load(in); } catch (Exception e) { throw new ExceptionInInitializerError("初始化properties失败!"); } } //根据Bean的名称获取bean对象 public static Object getBean(String beanName){ Object bean = null; try { String beanPath = properties.getProperty(beanName); bean = Class.forName(beanPath).newInstance(); } catch (Exception e) { e.printStackTrace(); } return bean; } }
-
对业务层实现类进行改进 AccountServiceImpl.java
//账户的业务层实现类 public class AccountServiceImpl implements IAccountService { // private IAccountDao accountDao = new AccountDaoImpl(); private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao"); public void saveAccount() { accountDao.saveAccount(); } }
-
对表现层进行改进 Client.java
//模拟一个表现层,用于调用业务层 public class Client { public static void main(String[] args) { // IAccountService accountService = new AccountServiceImpl(); for(int i=0;i<5;i++) { IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService"); // accountService.saveAccount(); System.out.println(accountService); } }
-
运行结果
- 目录结构
再次分析: 由运行结果可知, 对象是多例的,即被多次创建;
原因: 每次调用Class.forName(beanName).newInstance(), 都会调用默认构造函数创建新的对象
2.2.3 定义容器对工程再次改进
-
定义一个容器存储被创建的对象
-
对工厂类 BeanFactory.java 进行改进
public class BeanFactory { //定义一个Properties对象 private static Properties properties; //定义一个Map,用于存放我们要创建的对象,我们把它称之为容器 private static Map<String,Object> beans; //使用静态代码块为Properties对象赋值 static { try { //实例化对象 properties=new Properties(); //获取properties文件的流对象 InputStream in= BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"); properties.load(in); //实例化容器 beans = new HashMap<String, Object>(); //去除配置文件中所有的Key Enumeration keys = properties.keys(); //遍历枚举 while (keys.hasMoreElements()){ //取出每个key String key = keys.nextElement().toString(); //根据key获取value String beanPath = properties.getProperty(key); //反射创建对象 Object value = Class.forName(beanPath).newInstance(); //把key和value存入到容器中 beans.put(key,value); } } catch (Exception e) { throw new ExceptionInInitializerError("初始化properties失败!"); } } //根据bean的名称获取对象 public static Object getBean(String beanName){ return beans.get(beanName); } //根据Bean的名称获取bean对象 // public static Object getBean(String beanName){ // Object bean = null; // // try { // String beanPath = properties.getProperty(beanName); // bean = Class.forName(beanPath).newInstance(); //每次都会调用默认构造函数创建对象 // } catch (Exception e) { // e.printStackTrace(); // } // return bean; // } }
-
运行结果
分析: 由运行结果可知此时为对象是单例的