程序中的耦合和解耦
什么是程序的耦合
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差(降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
总结:在软件工程中,耦合指的就是指对象之间的依赖关系。对象之间的依赖程度越高,耦合度就越高。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。
降低程序之间的依赖程度,即降低程序之间的耦合度的过程就叫做解耦。
例如:早期的Jdbc操作中,在注册数据库驱动时,为什么采用的是Class.forName的方式,而不是采用DriverManager.registerDriver的方式?
public class TestJdbc {
public static void main(String[] args) throws Exception {
//1.注册数据库驱动
// DriverManager.registerDriver( new Driver() );
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接
//3.获取传输器
//4.发送sql到服务器执行并返回执行结果
//5.处理结果
//6.释放资源
}
}
除了DriverManager.registerDriver
会导致驱动注册两次外,更重要的是,如果使用这种方式,JDBC程序就会依赖于数据库的驱动类(MySQL的Driver类),如果后期程序因数据量和性能原因升级到Oracle数据库,就需要修改程序源代码——重新导入新的驱动类,这会增加很多不必要的麻烦!
而是用Class.forName
方式注册驱动,这样的好处是Jdbc程序不再依赖具体的驱动类,即使删除(或不导入)mysql驱动包,程序依然可以编译(当然不可能运行,因为运行时肯定需要依赖驱动)。
此时类中仅仅是将mysql驱动类的全限定类名写死在程序中(只是一个字符串),可以将这个字符串提取到配置文件中,后期可以通过修改配置文件(而不用修改程序代码)轻松的替换数据库产品。
使用工厂模式解耦
我们可以创建一个工厂, 使用工厂来创建对象. 而不是我们手动去new
在maven的resource下创建配置文件
EmpService=com.test.service.EmpServiceImpl
EmpDao=com.test.dao.EmpDaoImpl
编写工厂类
package com.test.factory;
import java.io.InputStream;
import java.util.Properties;
/**
* 使用工厂+接口+配置文件 (降低)程序之间的耦合性(也就是以来成都)
*
* 作用: 帮我们创建程序中所需要的对象
* (而不是通过new的方式获取对象, 因为new对象会造成耦合性提升)
* 目的: 当我们替换某一层(类)时, 不需要修改java源代码, 就可以实现
*
* 工程: BeanFactory
* 配置文件: config.properties (放在src/main/resource下)
* 配置文件中负责配置Service层的实现类和Dao层的实现类
* 也就是说, 配置的是哪一个实现类, 我们就获取哪一个实现类的示例
* 如果将来要替换实现类, 只需要修改配置文件中配置的实现类即可!
*
* 接口: EmpService, EmpDao
*/
public class BeanFactory {
private static Properties prop = null;
static {
// 为prop进行初始化
prop = new Properties();
try {
// 加载config.properties文件, 获取指向该文件的流对象
// 通过当前类的字节码对象获取加载当前类的加载器
ClassLoader loader = BeanFactory.class.getClassLoader();
InputStream in = loader.getResourceAsStream("config.properties");
prop.load(in);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据接口名(EmpService, EmpDao) 获取该接口对应的子类实例
*/
public static Object getBean(String key) {
try {
// 通过key(接口名)获取该接口对应了类的全限定类名
String className = prop.getProperty(key);
// 通过子类的全限定类名, 获取该子类的字节码对象, 通过字节码对象获取类的实例
Object obj = Class.forName(className).newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
现在我们创建对象可以使用, 不用每次都去new, 大大降低了程序的耦合性
private EmpService service = (EmpService) BeanFactory.getBean("EmpService");