程序的耦合
-
耦合:程序间的依赖关系
包括:1.类之间的依赖 2.方法间的依赖
-
解耦:降低程序间的依赖关系
-
实际开发中,应该做到,编译期不依赖,运行时才依赖。
-
解耦的思路:
第一步:使用反射来创建对象,而避免使用new关键字。
第二步:通过读取配置文件来获取要创建的对象全限定类名。
耦合示例及利用工厂模式解耦
为了更直观的展示编程中的耦合问题,下面通过一个简单的示例来进行分析和解决。首先创建一个新的maven工程,工程内的各个包和子包如图:
其中dao
包和service
包中包含持久层和业务层的代码,各自都拥有一个接口以及一个对应的实现类。ui
包中的Client
类模拟一个表现层,用于调用业务层。
持久层的IAccountDao
接口中的内容如下:
package com.xmy.dao;
/**
* 账户的持久层接口
* @author xmy
*/
public interface IAccountDao {
/**
* 模拟保存账户
*/
void saveAccount();
}
其实现类AccountDaoImpl
的代码如下:
package com.xmy.dao.impl;
import com.xmy.dao.IAccountDao;
/**
* 账户的持久层实现类
* @author xmy
*/
public class AccountDaoImpl implements IAccountDao {
public void saveAccount() {
System.out.println("保存了账户");
}
}
业务层的IAccountService
接口代码如下:
package com.xmy.service;
/**
* 账户业务层的接口
* @author xmy
*/
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
}
其实现类AccountServiceImpl
代码如下:
package com.xmy.service.impl;
import com.xmy.dao.IAccountDao;
import com.xmy.dao.impl.AccountDaoImpl;
import com.xmy.service.IAccountService;
/**
* 账户的业务层实现类
* @author xmy
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao= new AccountDaoImpl();
public void saveAccount() {
accountDao.saveAccount();
}
}
模拟表现层的ui
包下的Client
类的代码为:
package com.xmy.ui;
import com.xmy.service.IAccountService;
import com.xmy.service.impl.AccountServiceImpl;
/**
* 模拟一个表现层,用于调用业务层
* @author xmy
*/
public class Client {
public static void main(String[] args) {
IAccountService as = new AccountServiceImpl();
as.saveAccount();
}
}
运行结果如图:
虽然能够完好的运行,但是在实现类AccountServiceImpl
和Client
类中,都使用new关键字直接对应接口子类的实例,如果在上面的程序中将AccountDaoImpl
或者AccountServiceImpl
移除,会导致其他类中的代码编译错误,此时表现层和业务层,及业务层和持久层之间的依赖程度过高,如果将来替换某一层,很可能会造成其他层无法运行,只能通过修改程序代码保证程序运行,这样依赖就会提高维护成本以及造成不必要的麻烦。而在程序中new对象的方式造成了这种程序之间的依赖程度提升,即提升了程序之间的耦合性。
例如在AccountServiceImpl
类中,如果我们移除了AccountDaoImpl
类,那么编译器会直接报错。如图所示。
而为了解决这种情况,我们可以使用工厂模式+配置文件来进行解耦。
创建com.xmy.factory.BeanFactory类,用于创建各个层所需要的的对象。
package com.xmy.factory;
import java.io.InputStream;
import java.util.Properties;
/**
* ---------------------------------------------------------
* Bean: 可重用组件(计算机英语)
* JavaBean:使用Java语言编写的可重用组件,例如:service层、dao层等
* JavaBean:通常分为业务Bean和实体Bean
* 业务Bean:处理业务逻辑,service层、dao层
* 实体Bean:封装数据,例如,为了封装员工信息而编写的Emp实体类.
* ---------------------------------------------------------
* 解除耦合:
* (1)需要提供配置文件,在配置文件中配置service和dao的实现类
* 配置内容为:唯一标识=实现类的全限定类名(key=value结构)
* (2)通过工厂读取配置文件中配置的全限定类名,利用反射创建对象。
* xml配置文件、properties配置文件
*通过容器存放创建的对象,每一个bean名称对应唯一的对象,实现单例,提高运行效率
* @author xmy
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getSystemResourceAsStream("Bean.properties");
props.load(in);
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError("初始化properties失败");
}
}
/**
* 根据bean的名称获取对象
* @param beanName
* @param <T>
* @return
*/
public static <T> T getBean(String beanName){
public static <T> T getBean(String beanName){
T bean = null;
try {
String beanPath = props.getProperty(beanName);
bean = (T) Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
}catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
在resource
目录下创建一个Bean.properties
文件,配置内容如下:
accountService=com.xmy.service.impl.AccountServiceImpl
accountDao=com.xmy.dao.impl.AccountDaoImpl
将Cilent
和AccountServiceImpl
类中的通过new对象的形式获取对应接口子类的实例改为使用BeanFactory
工厂获取Service和Dao层的实例。如下:
IAccountService as = BeanFactory.getBean("accountService");
private IAccountDao accountDao = BeanFactory.getBean("accountDao");
通过工厂模式分别获取业务层和持久层的实例,解除了各层之间的依赖,降低了耦合性,在这种情形下,即使我们移除AccountDaoImpl
类或者AccountServiceImpl
类,也不会导致编译错误,这样,如果将来需要替换某一层时,只需要在完成替换后对Bean.properties
配置文件进行相应的修改即可,而不需要对原有的程序代码进行修改,降低了维护成本。