1、IoC的基础概念
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
——摘自百度百科
2、推导
显然上述的概念显得抽象,不得不点出一个问题:“控制反转”将谁的控制权反转,又给了谁?
直接了当的说,传统的程序主要的控制权在于程序本身,由程序员硬编码至程序中,换句话说,也可以说是由程序员控制。而客户的需求不断变化,每当新的需求提出,负责程序的工具人便需要修改源代码以适应用户的需求,这导致代码高度耦合。“我们为什么不把控制权交给用户呢?”,这个时候,控制反转(IoC)思想便应运而生。
将对象的创建这一权利从程序员手中转给用户,由用户决定生成的具体对象。
3、原型代码实现
这里举一个简单的例子:当不知道用户选择什么数据库来进行用户数据读取时(当然这只是个例子)
传统的实现
结构:
DAO层:
public interface UserDao {
String getUser();
}
public class UserDaoImpl implements UserDao{
@Override
public String getUser() {return "使用默认数据库获取用户";}
}
public class UserDaoMysqlImpl implements UserDao{
@Override
public String getUser() {return "使用Mysql数据库获取用户";}
}
public class UserDaoOracleImpl implements UserDao{
@Override
public String getUser() {return "使用Oracle数据库获取用户";}
}
Service层:
public interface UserService {
void getUser();
}
public class UserServiceImpl implements UserService{
//传统的使用接口,每次有新的需求就需要修改源代码
//当用户想用默认的方式时
private UserDao userDao=new UserDaoImpl();
//当用户想用Mysql
//private UserDao userDao=new UserDaoMysqlImpl();
//当用户想用Oracle
//private UserDao userDao=new UserDaoOracleImpl();
//当用户想用......
//由于采用的是硬编码(即写死代码),所以当用户需求不确定时,就需要不断地修改源码
@Override
public void getUser() {
System.out.println(userDao.getUser());
}
}
Test:
@Test
public void MyTest():{
UserServiceImpl userService=new UserServiceImpl();
userService.getUser();
}
Console控制台显示:
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe" ...
使用默认数据库获取用户
Process finished with exit code 0
正如前文所说,这种传统工厂模式实现的弊端在于对象的创建完全硬编码在程序中,对象的创建将由程序控制,此时需求一旦发生改变就需要修改源代码。
IoC原型实现
项目结构不变
DAO层(不变):
public interface UserDao {
String getUser();
}
public class UserDaoImpl implements UserDao{
@Override
public String getUser() {return "使用默认数据库获取用户";}
}
public class UserDaoMysqlImpl implements UserDao{
@Override
public String getUser() {return "使用Mysql数据库获取用户";}
}
public class UserDaoOracleImpl implements UserDao{
@Override
public String getUser() {return "使用Oracle数据库获取用户";}
}
Service层:
public interface UserService {
void getUser();
}
public class UserServiceImpl implements UserService{
private UserDao userDao;
//由set方法实现动态值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
System.out.println(userDao.getUser());
}
}
Test:
@Test
public void MyTest():{
UserServiceImpl userService=new UserServiceImpl();
UserDao userDao=null;
//现在想要使用哪种方式将取决于用户的选择
System.out.println("1.我想使用默认的方法进行获取用户数据");
System.out.println("2.我想使用mysql进行获取用户数据");
System.out.println("3.我想使用Oracle进行获取用户数据");
Scanner sc=new Scanner(System.in);
System.out.print("================\n输入你的选择:");
int choice = sc.nextInt();
switch (choice) {
case 1:userDao = new UserDaoImpl();break;
case 2:userDao = new UserDaoMysqlImpl();break;
case 3:userDao = new UserDaoOracleImpl();break;
default:break;
}
userService.setUserDao(userDao);
userService.getUser();
}
Console控制台显示:
"C:\Program Files\Java\jdk1.8.0_281\bin\java.exe" ...
1.我想使用默认的方法进行获取用户数据
2.我想使用mysql进行获取用户数据
3.我想使用Oracle进行获取用户数据
================
输入你的选择:2
使用Mysql数据库获取用户
Process finished with exit code 0
此时可以发现,通过在DAO层中添加一个set方法可以实现动态的值的注入,将对象的创建的控制权转换给用户,不再需要我们修改
4、总结
传统实现和IoC控制反转原型实现:
细心的老哥可能会发现,以上提到的都还只是IoC的原型实现,这并不是IoC的“完全体”,这里主要是想向大家展示IoC的思想。以上的实现表面上是在一定程度上缓解了以上问题,但实质上这种代码耦合并没有改变。通过IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML 文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,这可能就是“依赖注入”说法的来源了。
再有,这种IoC模式有没有让你想起一个熟悉的Java特性——反射,利用Java 的“反射”编程,根据XML中给出的类定义生成相应的对象。从实现来看,以前在工厂模式里写死了的对象,IoC模式改为配置XML文件,这就把工厂和要生成的对象两者隔离,极大提高了灵活性和可维护性。