IoC,全称 Inversion of Control ,中文称 控制反转。
首先要知道,什么是 控制(Control) ?要控制谁?或者说谁要被控制?在面向对象编程中,对象A内部依赖对象B,那么就需要在对象A中实例化一个对象B,到底谁来实例化对象B,那么谁就是对象B的 控制者(controller)。我们通常的做法是在对象A中实例化一个对象B,那么 控制权 也就在对象A中,因为对象A也可以选择不去实例化,控制权在需求方。
独木不成林。面向对象编程的特点就是以 对象 为基本单位。大家本着 谁用谁创建 的原则。比如:发布评论时,要先剔除敏感词,然后才能发布,发布功能依赖敏感词剔除功能:
package com.foovv.example;
/**
* 评论功能类
*
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public class Comment {
/**
* 引入评论语句预处理工具类,并主动实例化对象
*/
private CommentPretreatmentUtils commentPretreatmentUtils = new CommentPretreatmentUtils();
/**
* 发布评论
*
* @param comment 评语
* @return 返回true表示评论成功,返回false表示评论失败
*/
public boolean uploadComment(String comment) {
// 剔除敏感词
this.commentPretreatmentUtils.removesensitiveWords(comment);
... 其他操作 ...
}
}
package com.foovv.example;
/**
* 评论预处理工具类
*
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public class CommentPretreatmentUtils {
/**
* 删除评语中的敏感词
*
* @param comment 评语
* @return 删除明敏感词后的评论
*/
public String removesensitiveWords(String comment) {
... 其他操作 ...
return ...;
}
}
Comment 类 控制 (control) 着所依赖对象的实例化的工作,它们之间的耦合度大大增加,万一依赖的类发生了变化,对于被依赖类无疑是致命一击。对于 Comment 来说,它的工作就是发布或删除评论,至于要剔除敏感词或者增加某些语句与它无关,那么至于要注入什么对象实例,也不应该与它有关,它只需要使用被注入对象实例的方法罢了。
控制权的交接给统一的系统负责,该系统负责对象的实例化、传递引用、初始化值等。控制权就发生了变化,外国人称为 Inversion of Control,IoC,中文意思是 控制反转。
DI,全称 Dependency Injection ,中文称 依赖注入。
控制实例化对象的权利交给 统一管理系统 来负责了,如果我们需要其他被依赖的对象时怎么 获取 呢?错错错,压根不需要主动去获取,而是等待被注入所依赖的对象实例。
接着上面评论功能的代码,被依赖的 CommentPretreatmentUtils 不变,修改 Comment 类。
package com.foovv.example;
/**
* 评论功能类
*
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public class Comment {
/**
* 引入评论语句预处理工具类,并主动实例化对象
*/
private CommentPretreatmentUtils commentPretreatmentUtils;
/**
* 构造方法注入依赖对象
*
* @param commentPretreatmentUtils 被依赖对象
*/
public Comment(CommentPretreatmentUtils commentPretreatmentUtils) {
this.commentPretreatmentUtils = commentPretreatmentUtils;
}
/**
* 发布评论
*
* @param comment 评语
* @return 返回true表示评论成功,返回false表示评论失败
*/
public boolean uploadComment(String comment) {
// 剔除敏感词
this.commentPretreatmentUtils.removesensitiveWords(comment);
... 其他操作 ...
}
}
使用时:
public class Client {
public static void main(String[] args) {
// 通过构造方法直接注入被依赖的对象实例
Comment comment = new Comment(new CommentPretreatmentUtils());
comment.uploadComment("文章很不错,更多文章在:https://github.com/about-cloud/JavaCore");
}
}
这样还是有很强的依赖:
public class Comment {
/**
* Comment类还是依赖CommentPretreatmentUtils类,
* 如果要切换其他类还是很麻烦
*/
private CommentPretreatmentUtils commentPretreatmentUtils;
... 其他操作 ...
}
根据 迪米特法则 现在要将被依赖的类以接口化编程实现,降低耦合度,只对外暴露方法服务,屏蔽实现细节,UML图如下:
评论预处理工具对外服务的接口
package com.foovv.example;
/**
* 评论预处理工具对外服务类
*
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public interface CommentPretreatmentService {
/**
* 删除评论中的敏感词
*
* @param comment 评语
* @return 删除明敏感词后的评论
*/
String removesensitiveWords(String comment);
}
评论预处理工具的实现类
package com.foovv.example;
/**
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public class CommentPretreatmentUtils implements CommentPretreatmentService {
/**
* 删除评论中的敏感词
*
* @param comment 评语
* @return 删除明敏感词后的评论
*/
@Override
public String removesensitiveWords(String comment) {
return comment;
}
}
评论功能类:
package com.foovv.example;
/**
* 评论功能类
*
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public class Comment {
/**
* 评论预处理服务类
*/
private CommentPretreatmentService commentPretreatmentService;
/**
* 构造方法注入依赖对象
*
* @param commentPretreatmentService 被依赖对象
*/
public Comment(CommentPretreatmentService commentPretreatmentService) {
this.commentPretreatmentService = commentPretreatmentService;
}
/**
* 发布评论
*
* @param comment 评论
* @return 返回true表示评论成功,返回false表示评论失败
*/
public boolean uploadComment(String comment) {
// 剔除敏感词
this.commentPretreatmentService.removesensitiveWords(comment);
... 其他操作 ...
}
}
上面的 Comment 最多也就知道 CommentPretreatmentService 接口中的方法(这里不考虑静态变量和default语句),不会知道具体实现,而且切换具体实现类也很方法。
使用时:
public class Client {
public static void main(String[] args) {
// 通过构造方法直接注入被依赖的对象实例
// 被注入的实例对象一定要实现于CommentPretreatmentService接口
Comment comment = new Comment(new CommentPretreatmentUtils());
comment.uploadComment("文章很不错,更多文章在:https://github.com/about-cloud/JavaCore");
}
}
接上面的构造方法注入,区别是将构造方法注入改成setter方法注入,客户端使用时通过调用setter方法注入即可:
评论功能类:
package com.foovv.example;
/**
* 评论功能类
*
* @author 代码风水师 https://github.com/about-cloud/JavaCore
* @version jdk1.8
*/
public class Comment {
/**
* 评论预处理服务类
*/
private CommentPretreatmentService commentPretreatmentService;
/**
* CommentPretreatmentService 的 setter 方法
*
* @param commentPretreatmentService set 注入实例对象
*/
public void setCommentPretreatmentService(CommentPretreatmentService commentPretreatmentService) {
this.commentPretreatmentService = commentPretreatmentService;
}
/**
* 发布评论
*
* @param comment 评论
* @return 返回true表示评论成功,返回false表示评论失败
*/
public boolean uploadComment(String comment) {
// 剔除敏感词
this.commentPretreatmentService.removesensitiveWords(comment);
... 其他操作 ...
}
}
使用时:
public class Client {
public static void main(String[] args) {
// 评论功能实例
Comment comment = new Comment();
// setter 注入被依赖的实例对象
comment.setCommentPretreatmentService(new CommentPretreatmentUtils());
comment.uploadComment("文章很不错,更多文章在:https://github.com/about-cloud/JavaCore");
}
}
首先在使用前通过文件或其他方式来映射 依赖对象 与 被依赖对象 之间的关系,也包括属性默认值的填充等等,最后统一管理系统 会去处理,然后 统一管理系统 生成大量的 实例对象,屏蔽其他细节,在外界看来它就像一个存放对象的容器,所以常称为 IoC容器。最后 IoC容器 这个统一管理系统会负责管理依赖关系。