依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI),理解总结

5,001 阅读4分钟

原始文章出处,写的非常棒

依赖倒置

6 大设计原则之一

原则

1.上层模块不应该依赖底层模块,它们都应该依赖于抽象。
2.抽象不应该依赖于细节,细节应该依赖于抽象。

上下层

上层:业务层,要进行的操作,就是做什么
底层:逻辑层,数据层,实现细节,就是怎么做

人出门的例子

人出门的交通工具多种多样,Driver作为抽象类,人依赖于Driver,至于骑车还是坐车,都由Driver的实现类去处理,比如Bike,Car等,实现类就是细节

常规写法
public class Person {

    private Bike mBike;

    public Person() {
        mBike = new Bike();
    }

    public void chumen() {
        System.out.println("出门了");
        mBike.drive();
    }
}
增加接口
public class Person {

//  private Bike mBike;
//  private Car mCar;
//  private Train mTrain;
    private Driveable mDriveable;

    public Person() {
        mDriveable = new Train();
    }

    public void chumen() {
        System.out.println("出门了");
        //mBike.drive();
        //mCar.drive();
        //mTrain.drive();
        mDriveable.drive();
    }

}

一图胜千言

依赖倒置实质上是面向接口编程的体现

控制反转(IoC)

控制反转 IoC 是 Inversion of Control的缩写,意思就是对于控制权的反转

刚才是Person自己实例化Driver,现在在构造函数里传入Driver,这样无论出行方式怎么变化,Person类都不用动

控制反转
public class Person {

    private Driveable mDriveable;

    public Person(Driveable driveable) {
        this.mDriveable = driveable;
    }

    public void chumen() {
        System.out.println("出门了");
        mDriveable.drive();
    }
}

public class Test1 {

    public static void main(String[] args) {
        Bike bike = new Bike();
        Car car = new Car();
        Train train = new Train();
//      Person person = new Person(bike);
//      Person person = new Person(car);
        Person person = new Person(train);
        person.chumen();
    }
}

依赖注入(Dependency injection)

依赖注入,也经常被简称为 DI,其实在上一节中,我们已经见到了它的身影。它是一种实现 IoC 的手段。什么意思呢?

为了不因为依赖实现的变动而去修改 Person, 也就是说以可能在 Driveable 实现类的改变下不改动 Person 这个类的代码,尽可能减少两者之间的耦合。我们需要采用上一节介绍的 IoC 模式来进行改写代码.
这个需要我们移交出对于依赖实例化的控制权,那么依赖怎么办?Person 无法实例化依赖了,它就需要在外部(IoC 容器)赋值给它,这个赋值的动作有个专门的术语叫做注入(injection),需要注意的是在 IoC 概念中,这个注入依赖的地方被称为 IoC 容器,但在依赖注入概念中,一般被称为注射器 (injector)。

实现依赖注入有 3 种方式:

  1. 构造函数中注入
  2. setter 方式注入
  3. 接口注入
setter
public class Person {
    public void setDriveable(Driveable mDriveable) {
        this.mDriveable = mDriveable;
    }
}
接口方式注入
public interface DepedencySetter {
    void set(Driveable driveable);
}

class Person implements DepedencySetter{
    private Driveable mDriveable;

    @Override
    public void set(Driveable driveable) {
        this.mDriveable = mDriveable;
    }
}

这种方式和 Setter 方式很相似。有很多同学可能有疑问那么加入一个接口是不是多此一举呢?

答案肯定是不是的,这涉及到一个角色的问题。还是以前面的餐厅为例,除了外卖员之外还有厨师和服务员,那么如果只有外卖员实现了一个送外卖的接口的话,那么餐厅配餐的时候就只会把外卖配置给外卖员。

接口的存在,表明了一种依赖配置的能力。

在软件框架中,读取 xml 配置文件,或者是利用反射技术读取注解,然后根据配置信息,框架动态将一些依赖配置给特定接口的类,我们也可以说 Injector 也依赖于接口,而不是特定的实现类,这样进一步提高了准确性与灵活性。

总结

1.依赖倒置是面向对象开发领域中的软件设计原则,它倡导上层模块不依赖于底层模块,抽象不依赖细节。
2.依赖反转是遵守依赖倒置这个原则而提出来的一种设计模式,它引入了 IoC 容器的概念。
3.依赖注入是为了实现依赖反转的一种手段之一。
4.它们的本质是为了代码更加的“高内聚,低耦合”。

1.控制反转是设计模式,遵从了依赖倒置原则
2.依赖注入是实现控制反转的手段