依赖反转原则

控制反转(IOC)

举例说明

public class UserServiceTest {
  public static boolean doTest() {
    // ...
  }

  public static void main(String[] args) {//这部分逻辑可以放到框架中
    if (doTest()) {
      System.out.println("Test succeed.");
    } else {
      System.out.println("Test failed.");
    }
  }
}

这段代码基本的流程都是需要认为的控制,我们可以利用控制反转,将控制交给“框架”。

public abstract class TestCase {
  public void run() {
    if (doTest()) {
      System.out.println("Test succeed.");
    } else {
      System.out.println("Test failed.");
    }
  }

  public abstract boolean doTest();
}

public class JunitApplication {
  private static final List<TestCase> testCases = new ArrayList<>();

  public static void register(TestCase testCase) {
    testCases.add(testCase);
  }

  public static final void main(String[] args) {
    for (TestCase case: testCases) {
      case.run();
    }
  }

这时候我们只是注册了,其操作比如需不需要判断doTest我们就不做了。

把这个简化版本的测试框架引入到工程中之后,我们只需要在框架预留的扩展点,也就是 TestCase 类中的 doTest() 抽象函数中,填充具体的测试代码就可以实现之前的功能了,完全不需要写负责执行流程的 main() 函数了。 具体的代码如下所示:

public class UserServiceTest extends TestCase {
  @Override
  public boolean doTest() {
    // ...
  }
}

// 注册操作还可以通过配置的方式来实现,不需要程序员显示调用register()
JunitApplication.register(new UserServiceTest();

刚刚举的这个例子,就是典型的通过框架来实现“控制反转”的例子。框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程。程序员利用框架进行开发的时候,只需要往预留的扩展点上,添加跟自己业务相关的代码,就可以利用框架来驱动整个程序流程的执行。

这里的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程可以通过框架来控制。流程的控制权从程序员“反转”到了框架。

依赖注入(DI)

用一句话来概括就是:不通过 new() 的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。

举个例子

在这个例子中,Notification 类负责消息推送,依赖 MessageSender 类实现推送商品促销、验证码等消息给用户。我们分别用依赖注入和非依赖注入两种方式来实现一下。具体的实现代码如下所示:

// 非依赖注入实现方式
public class Notification {
  private MessageSender messageSender;

  public Notification() {
    this.messageSender = new MessageSender(); //此处有点像hardcode
  }

  public void sendMessage(String cellphone, String message) {
    //...省略校验逻辑等...
    this.messageSender.send(cellphone, message);
  }
}

public class MessageSender {
  public void send(String cellphone, String message) {
    //....
  }
}
// 使用Notification
Notification notification = new Notification();

// 依赖注入的实现方式
public class Notification {
  private MessageSender messageSender;

  // 通过构造函数将messageSender传递进来
  public Notification(MessageSender messageSender) {
    this.messageSender = messageSender;
  }

  public void sendMessage(String cellphone, String message) {
    //...省略校验逻辑等...
    this.messageSender.send(cellphone, message);
  }
}
//使用Notification
MessageSender messageSender = new MessageSender();
Notification notification = new Notification(messageSender);

依赖注入框架(DI Framework)

在采用依赖注入实现的 Notification 类中,虽然我们不需要用类似 hard code 的方式,在类内部通过 new 来创建 MessageSender 对象,但是,这个创建对象、组装(或注入)对象的工作仅仅是被移动到了更上层代码而已,还是需要我们程序员自己来实现。具体代码如下所示:

public class Demo {
  public static final void main(String args[]) {
    MessageSender sender = new SmsSender(); //创建对象
    Notification notification = new Notification(sender);//依赖注入
    notification.sendMessage("13918942177", "短信验证码:2346");
  }
}

在实际的软件开发中,一些项目可能会涉及几十、上百、甚至几百个类,类对象的创建和依赖注入会变得非常复杂。如果这部分工作都是靠程序员自己写代码来完成,容易出错且开发成本也比较高。而对象创建和依赖注入的工作,本身跟具体的业务无关,我们完全可以抽象成框架来自动完成。

我们只需要通过依赖注入框架提供的扩展点,简单配置一下所有需要创建的类对象、类与类之间的依赖关系,就可以实现由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情。

依赖反转原则(DIP)

High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.

高层模块(high-level modules)不要依赖低层模块(low-level)。高层模块和低层模块应该通过抽象(abstractions)来互相依赖。除此之外,抽象(abstractions)不要依赖具体实现细节(details),具体实现细节(details)依赖抽象(abstractions)。

所谓高层模块和低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层。在平时的业务代码开发中,高层模块依赖底层模块是没有任何问题的。实际上,这条原则主要还是用来指导框架层面的设计,跟前面讲到的控制反转类似。我们拿 Tomcat 这个 Servlet 容器作为例子来解释一下。

Tomcat 是运行 Java Web 应用程序的容器。我们编写的 Web 应用程序代码只需要部署在 Tomcat 容器下,便可以被 Tomcat 容器调用执行。按照之前的划分原则,Tomcat 就是高层模块,我们编写的 Web 应用程序代码就是低层模块。Tomcat 和应用程序代码之间并没有直接的依赖关系,两者都依赖同一个“抽象”,也就是 Sevlet 规范。Servlet 规范不依赖具体的 Tomcat 容器和应用程序的实现细节,而 Tomcat 容器和应用程序依赖 Servlet 规范。

小结

1. 控制反转实际上,控制反转是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。

2. 依赖注入依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。

3. 依赖注入框架我们通过依赖注入框架提供的扩展点,简单配置一下所有需要的类及其类与类之间依赖关系,就可以实现由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情。

4. 依赖反转原则依赖反转原则也叫作依赖倒置原则。这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。

原文地址:https://www.cnblogs.com/CherryTab/p/12100887.html

时间: 2024-10-12 04:56:34

依赖反转原则的相关文章

依赖反转原则DIP 与 asp.net core 项目结构

DIP 依赖反转原则 Dependency Inversion Principle 的定义如下: 高级别的模块不应该依赖于低级别的模块, 他们都应该依赖于抽象. 假设Controller依赖于Repository的实例/实现, 而不是interface: 这个例子里面Controller是高级别模块, Repository是低级别模块. 但是根据定义: 高级别的模块不应该依赖于低级别的模块, 他们都应该依赖于抽象. 那么如何解决这个问题呢? 那就是 从Repository中提炼出一个interf

依赖反转Ioc和unity,autofac,castle框架教程及比较

1.依赖倒置的相关概念 http://www.cnblogs.com/fuchongjundream/p/3873073.html IoC模式(依赖.依赖倒置.依赖注入.控制反转) 2.依赖倒置的方式 http://www.cnblogs.com/muzinian/p/3357741.html 于依赖反转原则.控制反转和依赖注入的抽象的初学者指南 3.主流ioc框架 http://www.cnblogs.com/bchp/articles/1527693.html http://www.cnbl

依赖倒置原则、控制反转和依赖注入

1.依赖倒置原则: 1)上层模块不依赖与下层模块,而是共同依赖于抽象模块(或者接口). 2)抽象的东西不能是具象,具象依赖于抽象. 2.控制反转(Inversion of Control): 是软件运行时的一种行为.比如:对象A依赖于对象B,但是在B并不是直接去创建A,而是从外界取得A.就是说 一个对象并不直接去创建它所以依赖的其他对象. 3.依赖注入(Dependency Injection): 是控制反转的一个具体实现.就像上面说的一样,A的创建不是直接在B中创建,而是通过某些框架(比如Au

对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解

.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合,并由此引申出IoC.DI以及Ioc容器等概念. 2.意图 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本. 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节

面向对象设计原则四:依赖倒置原则

依赖倒置原则(DIP)        定义:高层模块不应该依赖底层模块,两者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象. 好处:稳定性.可维护性.可扩展性. 概述:DI就是依赖倒置的意思,也可称为控制反转,我们以前编写结构化的程序当中,也就是C语言这样的语言时,高层模块依赖于底层模块,也就是调用者和被调用者的关系,调用者要依赖于被调用者,被调用者编写的一些功能和服务,会影响高层,一旦底层发生了变化,也就是被调用者发生了变化,就直接影响了高层也就是调用者.这样的设计,很难保证他的稳定性

设计模式:依赖反转

一直说依赖反转,但是没明白是不太清晰什么是依赖反转. 最近看了 <敏捷软件开发:原则.模式与实践>这本书中说的依赖反转,结合自己的常写的代码,有一点新的见解. 以咱们常见的手机充电器为例: 比如我有Nokia X2手机(nokia出品的安卓机,本人手机这块比较节省),手机正常来说需要依赖手机充电器来进行充电. 我的Nokia手机用的就是Nokia配套的充电器. 1 class NokiaPhone{ 2 private NokiaCharger charger; 3 4 public void

依赖反转(Dependency inversion principle)和控制反转(Inversion of Control)

有两个对象A和B,A some B  是A依赖于B,当B some A的时候是B依赖于A这就叫依赖反转: 这种依赖关系如果让程序员自己控制(new 对象),就会出现高耦合,控制反转(依赖注入)就是让这种依赖关系由第三方管理(eg:spring)而不是程序员自己管理. 名词解释 依赖:一种模型元素之间的关系的描述.例如类A调用了类B,那么我们说类A依赖于类B. 耦合:一种模型元素之间的关系的描述.例如类A调用了类B或类B调用了类A,那么我们说类A与类B有耦合关系. 耦合度:模型元素之间的依赖程度的

深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP

前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第5篇,依赖倒置原则LSP(The Dependency Inversion Principle ). 英文原文:http://freshbrewedcode.com/derekgreer/2012/01/22/solid-javascript-the-dependency-inversion-principle/ 依赖倒置原则 依赖倒置原则的描述是: A. High-level modules should not

对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解(转)

所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合,并由此引申出IoC.DI以及Ioc容器等概念. 面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本. 面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象.即使