IOC思想
IOC,Inversion of Control,控制反转。它是一种思想,重点在于理解它,转变自己的思想。本文的核心在于通过一个实际例子来理解控制反转的思想。
1.例子介绍
一般开发一个业务采用的是三层架构,即表示层(UI层)、业务逻辑层(服务层)、数据访问层(DAL)。这里主要关注服务层和数据访问层。
总的功能就是说:服务层想要获取用户信息,进行某种操作(比如验证),会调用数据访问层的对象来实现。
2.传统方式实现
2.1 实现方式
传统方式一般采取接口加实现类的方式。
对于服务层:用Service接口和Service1Impl实现类来表示。
对于数据访问层用Dao接口(Data Access Object)和Dao1Impl实现类来表示。(Dao,Data Access Object)
2.2 在IDEA中操作步骤
第一步:创建maven文件
第二步:为pom.xml配置文件添加依赖,导入MVC,即
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.7</version>
</dependency>
第三步:等待导入完成,完成结果参考如下
第四步:开始创建Java文件
主要是创建两个接口和两个实现类。最后的UserService1Impl实现类如下:
public class UserService1Impl implements UserService {
private Dao userDao=new Dao1Impl();
public void getUser() {
userDao.getUser();
}
}
注意:要导入相关包。
第五步:进行测试
创建test文件夹中创建MyTest.java文件,参考代码如下
public class MyTest {
public static void main(String[] args) {
//用户直接接触表示层,服务请求传到服务层
UserService userService = new UserService1Impl();
userService.getUser();
}
}
运行结果:
注意
这里用到了面向接口编程的设计模式哦,他的优点在于便于更换不同的实现类(针对于服务层的变化)。具体来说,假定现在要切换到另一个实现类 AdvancedUserServiceImpl
,则只需修改实例化的部分:
public class MyTest {
public static void main(String[] args) {
// 切换到新的实现类
UserService userService = new AdvancedUserServiceImpl();
userService.getUser();
}
}
public class AdvancedUserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
System.out.println("Advanced user service is getting user data...");
userDao.getUser();
}
}
3.结合IOC思想的方式实现
3.1 传统方式的分析
必须假定现实需求是不断变化的,因此数据层的接口和实现类可能需要很多种,比如不同数据库(mysql,oracal),不同功能(查询,删除,替换)等。会发现,需求一变,数据层代码增加,而服务层由于和数据层强耦合(个人理解为一一对应的联系起来),服务层代码也增加,一旦设计的接口有成千上百个咋办?这种设计思想是不好的。
比如我按传统方式,就需要新建如下的文件:
3.2 set方法分析
传统方式产生弊端的根源在于服务层和数据层的强耦合,而强耦合根源在于每次业务实现类都要new一个相应的数据层实现类。
所以根源就在这个new关键字上,更进一步地说:
如果直接在业务代码的设计地方来实例化对象,就一定会强耦合——就是说实例化对象的控制权在核心代码设计的地方,就一定会发生强耦合。
如何解决这个问题?关键就在于把实例化对象的控制权转交出去,一个比较好的方法就是set方法,他将实例化对象的控制权转交给了测试端(可以认为是一个假想的用户)。
3.3 set方法实现
要想办法不要主动创建对象,只是接受客户端是实例化的对象。参考如下代码
public class UserServiceImpl implements UserService {
private Dao userDao;
//利用set方法转交实例化对象控制权
public void setUserDao(Dao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
测试段代码如下:
public class MyTest {
public static void main(String[] args) {
//用户直接接触表示层,服务请求传到服务层
UserService userService = new UserServiceImpl();
((UserServiceImpl)userService).setUserDao(new Dao3Impl());
userService.getUser();
}
}
3.4 观察
可以看到,实例化对象的控制权移交了,数据层需求变化,客户端new的对象不同就行啦,不需要改变服务层的对象,大大减少了代码开发量。这就成功实现了IOC(控制反转)。
注意:这儿用了强制类型转换,其语法格式为:(指定类型)对象
4.总结
传统方式,实例化对象的控制权在程序员上;控制反转(比如set注入)后,实例化对象的控制权不在程序员身上了,由此带来的核心业务代码与需求处代码强耦合的问题就没有了,程序员可以更加专注于核心业务!
注意,本文是通过set方法实现控制反转的,正式的spring是用spring 容器来实现的,但思想是相同的。