依赖注入 DI 控制反转 IOC 概念 案例 MD
| 我的GitHub | 我的博客 | 我的微信 | 我的邮箱 | 
|---|---|---|---|
| baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com | 
控制反转 IOC
Inversion Of Control
什么是控制反转 ? 
简单的说,从主动变被动就是控制反转。
控制反转是一个很广泛的概念, 依赖注入是控制反转的一个例子,但控制反转的例子还很多,甚至与软件开发无关。
传统的程序开发,人们总是从 main 函数开始,调用各种各样的库来完成一个程序。这样的开发,开发者控制着整个运行过程。而现在人们使用框架(Framework)开发,使用框架时,框架控制着整个运行过程。
对比以下的两个简单程序。
1、简单java程序
public class Activity {
    public Activity() {
        this.onCreate();//------------开发者主动调用onCreate()方法------------
    }
    public void onCreate() {
        System.out.println("onCreate called");
    }
    public static void main(String[] args) {
        Activity a = new Activity();
    }
}2、简单Android程序
public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) { //------------框架自主调用onCreate()方法------------
        super.onCreate(savedInstanceState);
        System.out.println("onCreate called");
    }
}这两个程序最大的区别就是,前者程序的运行完全由开发控制,后者程序的运行由Android框架控制。
虽然两个程序都有个onCreate方法,但在前者程序中,如果开发者觉得onCreate名称不合适,想改为Init,没问题,直接就可以改; 相比下,后者的onCreate名称就不能修改,因为后者使用了框架,享受框架带来福利的同时,就要遵循框架的规则。
这就是控制反转。
可以说,控制反转是所有框架最基本的特征,也是框架和普通类库最大的不同点。
控制反转还有一个漂亮的比喻,好莱坞原则(Hollywood principle):
don't call us, we'll call you。
不要打电话给我们,我们会打给你(如果合适)
这是好莱坞电影公司对面试者常见的答复。
事实上,不只电影行业,基本上所有公司人力资源部对面试者都会说类似的话,让面试者从主动联系转换为被动等待。
依赖注入 Dependency injection
这里通过一个简单的案例来说明。在公司里有一个常见的案例:把任务指派个程序员完成。
Step1 设计
把这个案例用面向对象的方式来设计,我们创建两个类:Task 和 Phper (php 程序员)
public class Phper {
    private String name;
    public Phper(String name){
        this.name=name;
    }
    public void writeCode(){
        System.out.println(this.name + " is writing php code");
    }
}public class Task {
    private String name;
    private Phper owner;
    public Task(String name){
        this.name =name;
        this.owner = new Phper("zhang3");//-----------关键看这里-----------
    }
    public void start(){
         System.out.println(this.name+ " started");
         this.owner.writeCode();
    }
}测试:
public class MyFramework {
     public static void main(String[] args) {
         Task t = new Task("Task #1");
         t.start();
     }
}运行结果:
Task #1 started
zhang3is writing php code我们看一看这个设计有什么问题。
如果同事仰慕你的设计,要重用你的代码,你把程序打成一个类库(jar包)发给同事。现在问题来了:同事发现这个Task 类 和 程序员 zhang3 绑定在一起,他所有创建的Task,都是程序员zhang3负责,他要把一些任务指派给Lee4, 就需要修改Task的源程序, 如果没有Task的源程序,就无法把任务指派给他人。
而类库(jar包)的使用者通常不需要也不应该来修改类库的源码,我们很自然的想到,应该让用户来指派任务负责人,于是有了新的设计。
Step2 设计
Phper不变。
public class Task {
    private String name;
    private Phper owner;
    public Task(String name){
        this.name =name;
    }
    public void setOwner(Phper owner){//-----------关键看这里,可按需指派-----------
        this.owner = owner;
    }
    public void start(){
         System.out.println(this.name+ " started");
         this.owner.writeCode();
    }
}测试:
public class MyFramework {
     public static void main(String[] args) {
         Task t = new Task("Task #1");
         Phper owner = new Phper("lee4");
         t.setOwner(owner);//用户在使用时按需指派特定的PHP程序员
         t.start();
     }
}这样用户就可在使用时指派特定的PHP程序员。
我们知道,Task类依赖Phper类,之前,Task类绑定特定的实例,现在这种依赖可以在使用时按需绑定,这就是依赖注入(DI)。
这个例子中,我们通过方法setOwner注入依赖对象,另一个常见的注入方式是在Task的构造函数中注入:
public Task(String name,Phper owner){
    this.name = name;
    this.owner = owner;
}在Java开发中,把一个对象实例传给一个新建对象的情况十分普遍,通常这就是注入依赖,Step2 的设计实现了依赖注入。
我们来看看Step2 的设计有什么问题。
如果公司是一个单纯使用PHP的公司,所有开发任务都有Phper 来完成,这样这个设就已经很好了,不用优化。但是随着公司的发展,有些任务需要JAVA来完成,公司招了Java程序员,现在问题来了,这个Task类库的的使用者发现,任务只能指派给Phper,一个很自然的需求就是,Task应该即可指派给Phper也可指派给Javaer。
Step3 设计
我们发现不管Phper 还是 Javaer 都是Coder(程序员), 把Task类对Phper类的依赖改为对Coder的依赖即可。
新增Coder接口
public interface Coder {
   void writeCode();
}
修改Phper类实现Coder接口
public class Phper implements Coder {
   private String name;
   public Phper(String name){
      this.name=name;
   }
    @Override
   public void writeCode(){
      System.out.println(this.name + " is writing php code");
   }
}新类Javaer实现Coder接口
public class Javaer implements Coder {
   private String name;
   public Javaer(String name){
      this.name=name;
   }
    @Override
   public void writeCode(){
      System.out.println(this.name + " is writing Java code");
   }
}修改Task由对Phper类的依赖改为对Coder的依赖
public class Task {
    private String name;
    private Coder owner;
    public Task(String name) {
        this.name = name;
    }
    public void setOwner(Coder owner) {
        this.owner = owner;
    }
    public void start() {
        System.out.println(this.name + " started");
        this.owner.writeCode();
    }
}测试
public class MyFramework {
    public static void main(String[] args) {
        Task t = new Task("Task #1");
        Coder owner = new Phper("lee4");
        //Coder owner = new Javaer("Wang5");
        t.setOwner(owner);
        t.start();
    }
}现在用户可以和方便的把任务指派给 Javaer 了,如果有新的 Pythoner 加入,没问题,类库的使用者只需让 Pythoner 实现 Coder 接口,就可把任务指派给 Pythoner, 无需修改 Task 源码, 提高了类库的可扩展性。
总结
回顾一下,我们开发的Task类:
- 在Step1 中,Task与特定实例绑定(zhang3 Phper)
- 在Step2 中,Task与特定类型绑定(Phper)
- 在Step3 中,Task与特定接口绑定(Coder)
虽然都是绑定, 从Step1,Step2 到 Step3 灵活性、可扩展性是依次提高的。
Step1 作为反面教材不可取, 至于是否需要从 Step2 提升为 Step3, 要看具体情况。
依赖注入实现了控制反转的思想
依赖注入(DI)实现了控制反转(IoC)的思想,看看怎么反转的:
- Step1 设计中,任务 Task 依赖负责人 owner, 所以就主动新建一个 Phper 赋值给 owner(这里可能是新建,也可能是在容器中获取一个现成的 Phper,是新建还是获取无关紧要,关键是主动赋值)。
- 在 Step2 和 Step3 中, Task 的 owner 是被动赋值的,谁来赋值,Task 自己不关心,可能是类库的用户,也可能是框架或容器,Task交出赋值权,从主动赋值到被动赋值,这就是控制反转。
JAVA中依赖注入的几种方式
通过set方法注入
public class ClassA {
    ClassB classB;
    public void setClassB(ClassB b) {
        classB = b;
    }
}通过构造器注入
public class ClassA {
    ClassB classB;
    public void ClassA(ClassB b) {
        classB = b;
    }
}通过注解注入
public class ClassA {
    @inject ClassB classB;//此时并不会完成注入,还需要依赖注入框架的支持,如RoboGuice,Dagger2
    //...
    public ClassA() {}
}通过接口注入
接口
interface InterfaceB {
    void doIt();
}形式一
class A implements InjectB {
    ClassB classB;
    @Override
    public void injectB(ClassB b) {
      classB = b;
    }
}形式二
public class ClassA {
    InterfaceB clzB;
    public void doSomething() {
        clzB = (InterfaceB) Class.forName("...").newInstance();//根据预先在配置文件中设定的实现类的类名动态加载实现类
        clzB.doIt();
    }
}此种接口注入方式因为具备侵入性,它要求组件必须与特定的接口相关联,因此实际使用有限。
2019-5-12
依赖注入 DI 控制反转 IOC 概念 案例 MD的更多相关文章
- 依赖注入 DI 控制反转 IOC MD
		Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ... 
- ADO.NET         .net core2.0添加json文件并转化成类注入控制器使用    简单了解 iTextSharp实现HTML to PDF    ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下    C# AutoMapper 了解一下
		ADO.NET 一.ADO.NET概要 ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库 二.ADO.NET的组成 ①System.Data → DataTable, ... 
- 浅析“依赖注入(DI)/控制反转(IOC)”的实现思路
		开始学习Spring的时候,对依赖注入(DI)——也叫控制反转(IOC)—— 的理解不是很深刻.随着学习的深入,也逐渐有了自己的认识,在此记录,也希望能帮助其他入门同学更深入地理解Spring.本文不 ... 
- ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC  了解一下
		先简单了解一这个几个 名词的意思. 控制反转(IOC) 依赖注入(DI) 并不是某种技术. 而是一种思想.一种面向对象编程法则 什么是控制反转(IOC)? 什么是依赖注入(DI) 可以点击下面链接 ... 
- ASP.NET中IOC容器Autofac(依赖注入DI 控制反转IOC)
		IOC的一个重点是在程序运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI来实现的.Autofac则是比较流行的一款IOC容器. IoC和DI有什么关系呢?其实它们是同一个概念的不同角度 ... 
- 浅谈(IOC)依赖注入与控制反转(DI)
		前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ... 
- 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)  依赖注入和控制反转的理解,写的太好了。
		轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战 ... 
- 谈谈php依赖注入和控制反转
		要想理解php依赖注入和控制反转两个概念,就必须搞清楚如下的问题: DI--Dependency Injection 依赖注入 IoC--Inversion of Control 控制反转 1. ... 
- .NET Core的依赖注入[1]: 控制反转
		写在前面:我之前写过一系列关于.NET Core依赖注入的文章,由于.NET Core依赖注入框架的实现原理发生了很大的改变,加上我对包括IoC和DI这些理论层面的东西又有了一些新的理解,所以我在此基 ... 
随机推荐
- nginx 日志之 error_log
			Nginx错误日志平时不用太关注,但是一旦出了问题,就需要借助错误日志来判断问题所在. 配置参数格式:error_log /path/to/log level; Nginx错误日志级别 常见的错误日志 ... 
- Android Studio 点运行启用时,列表中不显示虚拟机,但是实际上在AVD Manager中已经添加了2个虚拟设备了
			Android Studio 点运行启用时,列表中不显示虚拟机,但是实际上在AVD Manager中已经添加了2个虚拟设备了 百度上找了一下方法, 情况出现:打开androidstudio,一直连接不 ... 
- Spring Cloud Zuul 概览
			什么是API网关 网关这个词其实是一个硬件概念.因为按照定义,网络网关出现在网络的边缘,所以防火墙和代理服务器等相关功能 往往与之集成在一起.在家庭网络 和小型企业中,宽带路由器通常充当网络网关.它将 ... 
- 禁用wordpress新编辑器,使用经典编辑器的方法
			新更新的wordpress推出勒个什么古腾堡编辑器?简直太难用了,怎么屏蔽古腾堡编辑器,如何使用wordpress的经典编辑器.有人说使用插件,比如ClassicEditor或者DisableGute ... 
- SpringBoot——Profile多环境支持
			1.多profile文件形式 主配置文件编写时, 文件名可以是application-{profile}.properties/yml 默认使用的application.properties的配置. ... 
- servlet是什么?servlet到底是啥?
			#说实话 这个鬼servlet我听说过它好多年了,但是我真的不知道它到底是干啥用的.内心里总觉得这是个很复杂的,绝对是让人难以理解的东西,我真的感觉自己很抗拒它,不想知道,不想去了解.可是我还是不得不 ... 
- ios 打包相关的那些报错
			这张图片是因为打包bitcode的时候出现了失败,重新打包即可 这个是因为电脑容量不足导致的无法启动模拟器,这个经常发生在128g的电脑并且同时开启多个模拟器的情况下 打完包后,苹果会给你发邮件告诉你 ... 
- thinkphp项目部署在phpstudy里的nginx上
			朋友的一个thinkphp做的项目,让我帮他部署一下的,LINUX服务器,用宝塔. 第一台服务器,装上宝塔,宝塔里装NGINX,PHP5.6,再建立网站,绑定域名,访问成功,一切正常! 昨天试着给另一 ... 
- Ubuntu16.04下配置VScode的C/C++开发环境
			博客转载:https://blog.csdn.net/weixin_43374723/article/details/84064644 Visual studio code是微软发布的一个运行于 Ma ... 
- SpringBoot入门-JPA(三)
			什么是JPA JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. pom.xml <par ... 
