天天看点

IoC控制反转基础入门1. 简介2. 控制反转 IoC3. 依赖倒转原则 DIP4. 依赖注入DI

文章目录

  • 1. 简介
  • 2. 控制反转 IoC
    • 2.1 概念的理解
    • 2.2 例子说明
  • 3. 依赖倒转原则 DIP
    • 3.1 定义
    • 3.2 例子
  • 4. 依赖注入DI
    • 4.1 依赖注入的类型
    • 4.2 例子 略

参考:https://www.tutorialsteacher.com/ioc

作者本人在学习Ioc时,始终不理解IoC的本质什么,很多中文资料在我看来比较难懂,于是搜索英文资料,终于找到一篇比较浅显易懂的文章。原文代码是C#写的,本人用Java重写。

1. 简介

你可能听说过以下几个名词:

中文 英文 英文缩写
控制反转 Inversion of Control IoC
依赖倒转原则 Dependency Inversion Principle DIP
依赖注入 Dependency Injection DI
IoC容器 Ioc Container

但是你可能不清楚他们之间的区别。

IoC控制反转基础入门1. 简介2. 控制反转 IoC3. 依赖倒转原则 DIP4. 依赖注入DI

如上图所示,IoC和DIP是处于上层的设计原则(Principle),也正因为是原则,所以没有提供具体的实现细节,这些原则是我们设计类时需要遵循的原则。DI是模式(Pattern),Ioc容器是框架(Framework)。

让我们先简单过一下上面的几个概念。

  • 控制反转 IoC

IoC是一种设计原则,它建议在面向对象设计中反转(Inversion)各种控制(Control),以达到在类之间的松耦合(Loose Coupling)。在这里,控制更多指的是类的附加职责,而不是它的主要职责,比如控制应用的流程或控制所依赖类的创建和绑定。

  • 依赖倒转原则 DIP

DIP同样可以达到类之间的松耦合,强烈建议同时使用IoC和DIP来达到松耦合设计。

DIP建议高层模块(high-level modules)不应该依赖低层模块(low-levle modules),两者都应该依赖抽象(abstaction, )。在java中,抽象指的是抽象类或者接口。

  • 依赖注入 DI

DI是一种设计模式(Pattern),用来实现IoC,以达到反转依赖类的创建的目的。

  • IoC容器

IoC容器是一种框架,用来管理应用程序中的自动依赖注入,因此我们程序猿可以节省体力。著名的spring中就提供IoC容器ApplicationContext。

下图所示,我们一步一步地学习从耦合类到松耦合类的设计过程。

IoC控制反转基础入门1. 简介2. 控制反转 IoC3. 依赖倒转原则 DIP4. 依赖注入DI

强耦合类 -> 使用工厂模式实现IoC -> 通过创建抽象实现DIP -> 实现DI -> 使用IoC容器 - > 松耦合类。

2. 控制反转 IoC

2.1 概念的理解

学习一个东西,首先了解概念。先解释一下控制反转的概念,分成控制和反转两个部分。

控制:更多指的是控制类的附加职责,而不是它的主要职责,比如控制应用的流程或控制所依赖类的创建和绑定。

反转:指的将附加职责交给其他类来完成,而不是自己来完成。

比如,你开车去上班,那么意味着你控制着车,反转就是你不自己开车,而是坐的士去上班,车的控制权从你的手上转移到了出租车司机,你就不用操心开车的事,不用操心车停哪里,不用操心车的保养维修等等,你只需要关注自己的工作即可。

个人的简单理解:聚焦于自己主要的职责,把次要的职责外包出去,以此达到类之间松耦合的目的。

2.2 例子说明

public class A {
    private B b;
    public A() {
        b = new B();
    }

    public void task() {
        // do something here
        b.someMethod();
        // do something here
    }
}
public class B {
    public void someMethod() {
        // do something
    }
}
           

如上面的代码,A调用B类的someMethod()方法,换句话说A依赖于B。A类直接控制B类的创建和生命周期。Ioc原则推荐反转控制,就是说把B类的创建交给其他类。如下代码:

public class A {
    private B b;
    public A() {
        b = Factory.getObjectOfB();
    }

    public void task() {
        // do something here
        b.someMethod();
        // do something here
    }
}
public class B {
    public void someMethod() {
        // do something
    }
}

public class Factory {
    public static B getObjectOfB() {
        return new B();
    }
}

           

你可以看到B类的创建已经交给Factory了,A只需要拿来用就行,专注于自己的主要职责task()方法。这就是控制反转。

3. 依赖倒转原则 DIP

3.1 定义

  • 高层模块不应该依赖底层模块,两者都应该依赖抽象;
  • 抽象不应该依赖细节,细节应该依赖抽象。

3.2 例子

public class CustomerBusinessLogic {
    public CustomerBusinessLogic() {
    }
    public String getCustomerName(int id) {
        DataAccess dataAccess = DataAccessFactory.GetDataAccessObj();
        return dataAccess.getCustomerName(id);
    }
}
public class DataAccess {
    public DataAccess() {
    }

    public String getCustomerName(int id) {
        // get it from DB in real app
        return "Dummy Customer Name";
    }
}

public class DataAccessFactory {
    public static DataAccess GetDataAccessObj() {
        return new DataAccess();
    }
}

           

以上的例子,我们用简单工厂方法实现IoC原则,但是CustomerBusinessLogic直接用DataAccess,还是紧耦合。

正如DIP定义,高层模块不应该依赖低层模块,所谓高层模块就是依赖其他模块的模块,例如这里的CustomerBusinessLogic,那么被依赖的DataAccess就是低层模块;另外一个原则是抽象不依赖细节,细节依赖抽象。这里的抽象是什么? 在java中,就是抽象类和接口。

public class CustomerBusinessLogic {

    private IDataAccess dataAccess;

    public CustomerBusinessLogic() {
        this.dataAccess = DataAccessFactory.GetDataAccessObj();
    }
    public String getCustomerName(int id) {
        return dataAccess.getCustomerName(id);
    }
}
public interface IDataAccess {
    String getCustomerName(int id);
}
public class DataAccess implements IDataAccess {
    public DataAccess() {
    }

    @Override
    public String getCustomerName(int id) {
        // get it from DB in real app
        return "Dummy Customer Name";
    }
}
public class DataAccessFactory {
    public static IDataAccess GetDataAccessObj() {
        return new DataAccess();
    }
}

           

如上,高层模块CustomerBusinessLogic和低层模块DataAccess都依赖于抽象IDataAccess,细节DataAccess依赖于抽象IDataAccess。

4. 依赖注入DI

DI是一种设计模式,使用DI,我们可以将类的创建和绑定在依赖类的外部完成。简单的说A依赖于B,B的创建和绑定在A类的操作都不在A类内完成,而是其他类中完成。

实现DI涉及到三种类:

  • client class:客户端类,依赖服务类的类;
  • service class:服务类,给client提供服务的类;
  • injector class:注入类,创建服务类,并且把服务类注入客户端类的类。
IoC控制反转基础入门1. 简介2. 控制反转 IoC3. 依赖倒转原则 DIP4. 依赖注入DI

4.1 依赖注入的类型

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

  • Constructor Injection:构造函数注入;
  • Property Injection:属性注入,也即setter方法注入;
  • Method Injection:方法注入,

4.2 例子 略