设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)

原定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)

其核心思想是:要面向接口编程(IOP),而不是面向实现编程

本文分别从:编码层;模块层;服务层。三个维度的案例,介绍DIP在实际项目中的应用

代码级

在java中需要遵循如下原则: 
1. 模块间的依赖通过抽象类或接口发生,实现类之间的依赖关系也是通过抽象类或接口产生(实现类之间不应发生直接的依赖关系),降低系统的耦合性 
2. 接口或抽象不依赖于实现类,但实现类依赖接口或抽象类,实现类对系统需要的功能具体实现,提高类的内聚程度

静态:静态代码,实现类具体依赖抽象,例实现类要实现接口,则自然依赖接口类。所以DIP讲的“细节应该依赖抽象”是在代码实现上的依赖,而非实例化或运行时

动态:运行时,则是抽象方法依赖具体实现方法的处理。是一种反向依赖

未使用DIP前:

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第1张图片

 public class Bens {
    public String run(){
      return "this is Bens";
    }
  }

  public class BMW {
    public String run(){
      return "this is BMW";
    }
  }


  public static class Driver{
    public void drive(Bens bens){
      bens.run();
    }
    //有新汽车类型,需要重载改方法
    public void drive(BMW bmw){
      bmw.run();
    }

    public static void main(String[] arg) {

      Driver driver = new Driver();
      //开奔驰
      driver.drive(new Bens());
      //开宝马
      driver.drive(new BMW());
    }
  }

使用DIP后:

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第2张图片


  public interface Car{
    public String run();
  }
  
  public class Bens implements Car{
    @Override
    public String run(){
      return "this is Bens";
    }
  }

  public class BMW implements Car{
    @Override
    public String run(){
      return "this is BMW";
    }
  }


  public static class Driver{
    //有新汽车类型,无需改动
    public void drive(Car car){
      car.run();
    }

    public static void main(String[] arg) {

      Driver driver = new Driver();
      //开奔驰
      driver.drive(new Bens());
      //开宝马
      driver.drive(new BMW());
    }
  }

分析

核心的变化是将变更的范围缩小。新增/修改汽车类型,将原来需要修改使用类(Driver)的使用方法(drive)消除掉了。

除了这种明显的代码解构,Spring的IOC也是依赖反转的典型应用场景

模块级

这里举两个例子:一是服务接口暴露;而是领域驱动(DDD)中对领域模型的依赖。

我们先看第一个

反模式DIP

  • demo-dip 作为一个spring boot的spring cloud服务
  • 同时将demo-dip作为client包.jar 分发给依赖该服务的调用方使用

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第3张图片

 不完善的DIP

  • client端(抽象接口)从实现中分离
  • 接口的部分实现依然保留在client端,不够干净

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第4张图片

 DIP

接口定义和实现在模块层完全分离

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第5张图片

案例二,DDD的经典分层架构为

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第6张图片

实际上我们为了以领域模型定业务 ,采用依赖倒置原则后,使领域层和基础设施层都只依赖于由领域模型所定义的抽象接口

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第7张图片

服务级

我们举一个使用MQ让服务依赖反转来降低耦合的案例

假如我们有一个新零售系统,其中有三个子系统

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第8张图片

  •  订单系统:负责接单
  • 拣货系统:负责生成拣货单,并通知拣货人员
  • 运力系统:负责创建配送单,分配配送运力

在上图架构模式下,依赖关系至上而下,订单系统强依赖拣货系统和运力系统。 一旦下游两个系统出现任何问题,上游将无法接单。

我们选择利用MQ,实现依赖反转。调整一下架构:

设计模式-- 依赖倒置DIP(例解-从编码到服务级解耦)_第9张图片

 此时订单不再直接依赖下游系统,它终于可以放飞自我,任意接单了。相反,下游系统则要反向依赖订单消息。该模式下,订单message变为“接口”(记住DIP核心是鼓励我们面向接口编程)

最后

从系统级维度看,如果有以下的情况,都可以考虑反转依赖,降低耦合

(1)变动方是A系统,却需要BCD配合(如,数据库IP调整,各使用方需要修改配置重启)

        优化:增加中间层,让各方依赖反转,如数据库IP连接问题,增加内容DNS系统

行业有个至理名言:计算机系统中没有一个问题是不能通过加一个中间层解决的


(2)需求方是A系统,改动方却在BCD

        优化:系统拆分,服务重新划分。特性业务上浮,共性业务下沉。 

你可能感兴趣的:(架构,依赖倒置原则,DIP,SOLID)