终于有人把控制反转和依赖注入讲清楚了

可能你不了解 控制反转依赖注入,但是你却一直在使用它

使用过Spring的老铁,应该都听说过 “控制反转”“依赖注入” ,也就是常说的IOC 和 DI,这两个概念通常会一起出现,那么这两者是一个概念吗?又该如何作区分,接下来我们就来研究一下,他俩到底是什么东西。

在正式开始之前,我先提出几个问题,然后我们带着问题开始接下来的研究。

1.控制反转中的”控制”,具体指什么?反转又是从哪里反转到了哪里?

2.依赖注入中的依赖是什么?注入又是怎么实现的?

3.依赖注入和控制反转有什么区别呢?

控制反转

控制反转的英文翻译是 Inversion Of Control,对这个陌生没关系,它的简写你一定很熟悉,那就是IOC,不过这个IOC和Spring中的Ioc容器还不太一样,后面我们详细说明。

简单来说:IOC是一种框架设计思想,而spring的Ioc容器是一个具体的技术实现。

为了更好的说明IOC的思想,我们先看一下下面的例子:

非控制反转

public class TestCaseOne {
    public boolean testOne() {
        return true;
    }
}
public class TestCaseTwo {
    public boolean testTwo() {
        return true;
    }
}
public class Main {
    public static void main(String[] args) {
        TestCaseOne one = new TestCaseOne();
        TestCaseTwo tow = new TestCaseTwo();
        if(one.testOne()) {
            System.out.println("test successful.");
        } else {
            System.out.println("test failed.");
        }
        if(tow.testTwo()) {
            System.out.println("test successful.");
        } else {
            System.out.println("test failed.");
        }
    }
}

控制反转

public abstract class TestCaseBase {
    public abstract boolean test();
}

public class TestCaseOne extends TestCaseBase{

    @Override
    public boolean test() 
{
        return true;
    }
}

public class TestCaseTwo extends TestCaseBase{

    @Override
    public boolean test() 
{
        return true;
    }
}

public class Application {

    private static List<TestCaseBase> caseContainer = new ArrayList<>();

    public static void addTestCase(TestCaseBase testCase) {
        caseContainer.add(testCase);
    }


    public static void runTestCase() {
        for (TestCaseBase testCase : caseContainer) {
            if(testCase.test()) {
                System.out.println("test successful.");
            } else {
                System.out.println("test failed.");
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Application.addTestCase(new TestCaseOne());
        Application.addTestCase(new TestCaseTwo());

        Application.runTestCase();
    }
}

在上面两段代码中,对比两个Main类中的main方法,你能看出什么差异吗?

上面两段代码实现的功能是一样的,都是执行测试用例,不过两者实现的方式差异却很大。

在第一段代码中,每一个测试用例的被测试方法,在main方法中,从上到下逐个被调用,调用代码是程序员自己编写的,也就是整个测试用例的执行流程中的“控制流”—— 调用者调用被调用的过程,完全是程序员自己控制的。

而在第二段代码中,程序员需要做的事情主要是将测试用例添加到测试框架中,然后运行测试框架,具体测试用例中的测试方法以什么样的方式运行,以及在什么时间点被调用,程序员是不知道的, 也就是测试用例执行流程中的"控制流",不是程序员控制的,而是框架控制的

到这里,我们就可以回答上面关于控制反转的两个问题了。

“控制” 是指对程序执行流程的控制权,也就是调用者,在什么时候以什么方式调用被调用者的流程。

控制反转是指将流程的控制权从程序员手中转移到了框架里。

控制反转有什么好处呢?

使用控制反转的思想,可以让程序员更加专注编写业务代码,无需关注非业务代码运行流程的细节,可以提高工作效率,这也是oop中封装原则的初衷。

控制反转的思想在绝大部分的框架中都有应用。我们在使用第三方框架时,只需要根据框架的使用说明,将业务相关的代码”关联”(类似于依赖注入代码中的addTestCase)到框架中即可,而无需关心我们的业务代码,在框架中是何时被调用的。

有些框架将IOC的思想应用的非常好,将具体的”控制流”封装的很深,只阅读框架的源码很难理清楚整个调用流程,还需要结合ide的调试工具的辅助。阅读过框架源码的小伙伴一定深有体会吧。

总结来说,控制反转是一种设计思想,用来指导应用框架的设计,是“道”层面的内容。

依赖注入

依赖注入也是我们经常听到一个概念,对于不了解它的读者来说,感觉很高大上,如果你了解它后,就会觉得 “就这?”,不信接着往下看。

依赖注入简单来说就是:不通过new()方法在类内部创建被依赖的对象,而是将被依赖的对象,在外部创建好,通过依赖对象的构造方法或者其他参数设置方法,将被依赖的类给传递进来。

所以开头关于依赖注入的问题,现在也可以回答了:

依赖:就是 被new出来的对象,或者被需要的组件。

注入:就是一个将被依赖的组件传递到被依赖的组件中的过程。

为了更好的说明依赖注入,我们还是写一段代码来说明一下。

非依赖注入代码:

public abstract class Vehicle {

    public abstract void drive();
}

public class Car extends Vehicle {
    @Override
    public void drive() 
{
        System.out.println("drive car.");
    }
}

public class GoHome {

    private Vehicle vehicle ;

    public GoHome() {
        vehicle = new Car();
    }

    public void go() {
        vehicle.drive();
    }
}

依赖注入代码:

public class GoHomeWithDi {

    private Vehicle vehicle ;

    public GoHomeWithDi(Vehicle vehicle) {
        this.vehicle = vehicle;
    }

    public void go() {
        vehicle.drive();
    }
}

在上面代码中,依赖对象是GoHomeGoHomeWithDi,被依赖对象是 vehicle ,在非依赖注入代码中,vehicle是代码中写死的,那么上面代码表达的语义就是 **”只能开车回家”**。

而依赖注入的实现方式,是通过依赖对象的构造方法将被依赖对象注入进来。表达的语义是 “想怎么回家就怎么回家”,因为回家的乘坐的交通工具没有写死,可以选择的方式很多。

相比控制反转,依赖注入是一种具体的编程技巧,是 “术” 层面的内容。

到这里我想你应该可以回答开头的第三个问题了,控制反转和依赖注入,不是一个层面的内容

在实际的工作中,一个项目里可能会有成百上千的类,类之间存在比较复杂的依赖关系,如果这些依赖关系,使用上面依赖注入的实现方式的话,出现错误的可能性会比较大,而且维护成本也比较高,再加上依赖注入和具体业务关系也不大,依赖注入的过程完全可以结合控制反转的思想 “反转” 给框架来实现。

而Spring的Ioc容器就是一款比较优秀的依赖注入框架。既然如此,为什么spring还自称是IOC容器(控制反转容器)呢?

其实spring的容器,被称为控制反转容器,还是依赖注入框架都没有错,使用控制反转容器,是描述这个框架的设计思想,将依赖注入的流程反转给了框架,而称之为依赖注入框架,是因为spring的这个容器就是为了解决这个依赖注入的问题,用依赖注入框架来描述更具体,也更具有针对性。

今日分享如果对你有帮助,帮忙点个在看

原文始发于微信公众号(小李哥编程):终于有人把控制反转和依赖注入讲清楚了

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/268029.html

(0)
码上实战的头像码上实战

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!