共计 4316 个字符,预计需要花费 11 分钟才能阅读完成。
推荐学习:
这可能是全网 Java 学习路线最完整,最详细的版本了,没有之一
1、聊聊解耦?
耦合:代码之间的关联关系称为耦合,具有强关联关系的称为强耦合。
解耦:解除代码之间的关联关系,使每个业务代码职责单一,目的明确,通常我们在业务上称为解耦。
2、代码示例
我们以传统的 EJB 开发模式为例子,先不以框架展示,大家可以看看一些改代码难受的场景。
业务来了:我需要把一段数据保存到 mysql 数据库中,按照分层逻辑实现(controller,service,dao)
Dao 接口层:
publicinterfaceUserDao{
/**
* 保存的接口方法
*/
voidsave();
}
Dao 的 mysql 实现:
publicclassUserDaoMysqlImplimplementsUserDao{
@Override
publicvoidsave() {
System.out.println(“ 保存 mysql 数据库成功!”);
}
}
Service 接口层:
publicinterfaceUserService{
/**
* 业务接口保存方法
*/
voidsave();
}
Service 的实现:
publicclassUserServiceImplimplementsUserService{
// 业务层调用数据 dao 层,这里不解释了
privateUserDao userDao =newUserDaoMysqlImpl();
@Override
publicvoidsave() {
userDao.save();
}
}
Controller 视图层:
publicclassUserController{
privateUserService userService =newUserServiceImpl();
publicvoidsave(){
userService.save();
}
}
很明显,我们已经实现了业务功能:保存一段数据进 mysql 数据库
这个时候,你的产品经理说,客户 mysql 坏了,刚装了个 oracle,你再改改吧?
然后你这个时候也就加个 oracle,实际上不费时,需求我们再补充下
现在需求:保存一段数据,可以保存在 mysql,也可以保存在 oracle
上面已经有 mysql 代码了,我们可以知道,我需要增加个 dao 的实现,称为 UserDaoOracleImpl
上代码先:
publicclassUserDaoOracleImplimplementsUserDao{
@Override
publicvoidsave() {
System.out.println(“ 保存 oracle 数据库成功!”);
}
}
ok 我们还要改一个地方,就是 UserServiceImpl,之前父类接口指向的子类引用要改成 oracle
publicclassUserServiceImplimplementsUserService{
// 业务层调用数据 dao 层,这里不解释了
// private UserDao userDao = new UserDaoMysqlImpl();
privateUserDao userDao =newUserDaoOracleImpl();
@Override
publicvoidsave() {
userDao.save();
}
}
我们发现,在目前的需求形式上,dao 的扩展我们是一定会需要改的,因为满足多态的特性,但是我们增加一个 dao 层的某个业务,连 service 业务层代码也要动,你想想,如果业务层代码达到了上千,即便你有了注释,改了某一层,另一层也要跟着改动,是不是很难受?
所以我们目前要解决:(如果每次 dao 进行扩展都去 service 修改源码来切换到新的 dao,这样做法耦合度太高,每次都需要去修改 UserServiceImpl 的代码)
这个场景,我们很经典的称为耦合!!!!我们可以自己给耦合多一个定义
耦合: 代码之间的关联关系称为耦合,更具体的说,在当前主流的职责划分层次(controller,service,dao)明确的前提下进行编码,某一层的改动,会导致另一个层跟着变动,可以称为耦合。
3、工厂模式可以解耦
在不了解工厂模式的前提下,就默认把这个作为生产对象的地方;
我们新建一个工厂,称为 BeanFactory
/**
* 定义一个 bean 工厂,专门生产普通对象
*/
publicclassBeanFactory{
publicstaticUserDaogetBean() {
returnnewUserDaoMysqlImpl();
}
}
然后我们要对耦合的那一层修改,目前看是把 service 解耦了吧?我所有创建对象的操作,都在一个具体的工厂类里;
publicclassUserServiceImplimplementsUserService{
// 业务层调用数据 dao 层,这里不解释了
// private UserDao userDao = new UserDaoMysqlImpl();
// private UserDao userDao = new UserDaoOracleImpl();
privateUserDao userDao = BeanFactory.getBean();
@Override
publicvoidsave() {
userDao.save();
}
}
很明显,我已经进行了 service 和 dao 解耦;但是!!!!!!
该死的需求是:我要改成 oracle,sqlserver,……
那我们继续:我顺便把之前的 getBean 换成了 mysql 的
publicclassUserServiceImplimplementsUserService{
// 业务层调用数据 dao 层,这里不解释了
// private UserDao userDao = new UserDaoMysqlImpl();
// private UserDao userDao = new UserDaoOracleImpl();
// private UserDao userDao = BeanFactory.getMysqlBean();
privateUserDao userDao = BeanFactory.getOracleBean();
@Override
publicvoidsave() {
userDao.save();
}
}
很明显我现在已经把 dao 和 service 的耦合,转移到了工厂和 service 上了,这就是解耦的一步;但是大家还是疑惑,我感觉我代码增多了?或者更麻烦了?我们继续看
我们每次增加新的业务,扩展,都要修改工厂,所以这个我们可以不可以不在代码里直接做这个事情?——– 引出配置文件
我们在 resources 下定义一个 applicationContext.properties
userDao=com.chenxin.gmall.user.demo.dao.UserDaoMysqlImpl
userService=com.chenxin.gmall.user.demo.service.UserServiceImpl
如果我们需要换成 oracle,我们只改这个配置文件,改成 UserDaoOracleImpl,不需要去动代码
那我们刚刚的 BeanFactory 就又可以通过读取配置文件的方式,用反射来创建对象,反射创建对象是根据对象的全类名做的,不是直接 new,看看效果
/**
* 定义一个 bean 工厂,专门生产普通对象
*/
publicclassBeanFactory{
privatestaticProperties properties =newProperties();
//1. 加载配置文件
static{
InputStream resourceAsStream = BeanFactory.class.getResourceAsStream(“/applicationContext.properties”);
try{
properties.load(resourceAsStream);
resourceAsStream.close();
}catch(IOException e) {
e.printStackTrace();
}
}
//publicstaticUserDaogetMysqlBean() {
// return new UserDaoMysqlImpl();
// }
//
// public static UserDao getOracleBean() {
// return new UserDaoOracleImpl();
// }
publicstaticUserDaogetBean(String key){
// 2. 使用 key 获得 value
String className = properties.getProperty(key);
// 3. 使用 value 利用反射技术创建对象
try{
return(UserDao) Class.forName(className).newInstance();
}catch(Exception e){
e.printStackTrace();
returnnull;
}
}
}
好了,我们来需求,我们要保存到 oracle,sqlserver 数据库,看看你是不是只需要多一个 dao 的 oracle 实现,然后去改配置文件就 ok?多一个 sqlserver 后,改下 applicationContext.properties
算了我直接写个代码吧:
/**
* 新增了 sqlserver 的支持,多态的表现
*/
publicclassUserDaoSqlServerImplimplementsUserDao{
@Override
publicvoidsave() {
System.out.println(“ 保存 SqlServer 数据库成功!”);
}
}
改下 applicationContext.properties
userDao=com.chenxin.gmall.user.demo.dao.UserDaoSqlServerImpl
userService=com.chenxin.gmall.user.demo.service.UserServiceImpl
你可以试一下,是不是这么干的!
现在看来,是解耦了不少吧。但是会有人发现吗,这个 getBean 返回值,是 UserDao,如果你有很多,是不是我们要写很多很多的 Dao 的 getBean?别急,后面一步一步带你走向 spring 的思路
最后一句话,解耦不代表代码一定少,更多时候是你用更多的代码来解决人力成本,所以新手一定要记得,解耦的原则,是减少开发中出现的问题,增加开发效率,不代表代码一定会减少下去,希望不要有这样的误区!
感谢阅读,三连是最大的支持!
丸趣 TV 网 – 提供最优质的资源集合!