Design Patterns - Three: Dependency Inversion Principle


@

What is the Dependency Inversion Principle?


Take a look at the original definition Dependency Inversion principle:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions

Translate, contains three definitions:

● high-level modules should not depend on low-level modules, both of which should rely on its abstract;
● abstract should not rely on details;
● details should depend on the abstract.

Lower layer module and a module readily appreciated, is implemented by the logic of each logic composed of atoms, indivisible atomic logic block is low level, the logic of atomic layer module is reassembled.

Abstract and details of it? In the Java language, it refers to the abstract interface or abstract class, both can not be directly instantiated; DetailsDetails class is class is implemented, or implemented interfaces inherit the abstract class is generated, which is characterized by may be directly instantiated .

Dependency Inversion Principle performance in the Java language is:
● dependencies between modules occurs through abstraction, no direct dependencies between classes, which dependency is generated by the interface or abstract class;
● interface or abstract class does not depend in the implementation class;
● implementation class dependent interface or abstract class.


Why use Dependency Inversion Principle?

Dependency Inversion Principle can be reduced using the coupling between classes, and improve system stability and reduce the risk due to parallel development, improve the readability and maintainability of the code.

Mercedes-Benz to chauffeur-driven cars, for example:


3-1: chauffeur-driven Mercedes class diagram

在这里插入图片描述

Mercedes-Benz provides a method run, run on behalf of the vehicle.
Mercedes categories:

public class Benz { 
 //汽车会跑 
  public void run(){ 
    System.out.println("奔驰汽车开始运行..."); 
   }
}


Mercedes driver starts by calling the run method Mercedes.
The driver class:

public class Driver { 
  //司机的主要职责就是驾驶汽车
   public void drive(Benz benz){ 
   benz.run();
   }
 }


有车,有司机,在Client场景类产生相应的对象.
场景类:

public class Client { 
  public static void main(String[] args) { 
    Driver zhangSan = new Driver();
    Benz benz = new Benz(); //张三开奔驰车 
    zhangSan.drive(benz);
   } 
 }


以上实现了司机开车的场景,在功能上是没有问题。

现在新的需求来了,司机不仅要开奔驰车,还要开宝马车。

宝马车类:

public class BMW { 
  //宝马车当然也可以开动了
   public void run(){ 
     System.out.println("宝马汽车开始运行..."); 
    }
 }


现在发现问题了,司机没有开动宝马车类的方法,怎么办?给司机类添加开宝马车的方法吗?如果接下来司机要开挖掘机呢?在这里,司机类和奔驰车类之间是紧耦合的关系,其导致的结果就是系统的可维护性大大降低。

这时候就应该引入依赖倒置原则。


3-2:引入依赖倒置原则后的类图

在这里插入图片描述

建立两个接口:IDriver和ICar,分别定义了司机和汽车的各个职能,司机实现现drive()方法。

司机接口:

public interface IDriver { 
 //老司机,会开车
  public void drive(ICar car); 
}


司机实现类:实现IDriver接口:

public class Driver implements IDriver{
  //司机的主要职责就是驾驶汽车 
  public void drive(ICar car){
     car.run();
 } 
}


在IDriver中,通过传入ICar接口实现了抽象之间的依赖关系,Driver实现类也传入了ICar 接口,至于到底是哪个型号的Car,需要在高层模块中声明。

汽车接口及两个实现类:

public interface ICar { 
 public void run();
 }

//奔驰汽车类
public class Benz implements ICar{ 
  public void run(){ 
  System.out.println("奔驰汽车开始运行...");
 } 
}

//宝马汽车类
public class BMW implements ICar{ 
  public void run(){ 
    System.out.println("宝马汽车开始运行...");
  } 
}


业务场景类:

public class Client { 
  public static void main(String[] args) { 
    IDriver zhangSan = new Driver(); 

    ICar benz = new Benz();
    //张三开奔驰车
     zhangSan.drive(benz); 

    ICar bmw = new BMW(); 
    //张三开宝马车
    zhangSan.drive(bmw);

   } 

}

在新增加低层模块时,只修改了业务场景类,也就是高层模块,对其他低层模块如 Driver类不需要做任何修改,业务就可以运行,把“变更”引起的风险扩散降到最低。


依赖的三种写法

依赖是可以传递的,A对象依赖B对象,B又依赖C,C又依赖D——只要做到抽象依赖,即使是多层的依赖传递也是没有丝毫问题的。

对象的依赖关系有三种方式来传递,如下所示:

1.构造函数传递依赖对象

在类中通过构造函数声明依赖对象,按照依赖注入的说法,这种方式叫做构造函数注入,按照这种方式的注入,对IDriver和Driver进行修改。

public interface IDriver { 
  //司机就会开车
   public void drive(); 
}

public class Driver implements IDriver{
  private ICar car; 
  //构造函数注入 
  public Driver(ICar _car){ 
    this.car = _car; 
  }
  //司机的主要职责就是驾驶汽车 
  public void drive(){ 
    this.car.run(); 
  } 

}


2、Setter方法传递依赖对象

在抽象中设置Setter方法声明依赖关系,依照依赖注入的说法,这是Setter依赖注入,按照这种方式的注入,对IDriver和Driver进行修改:

public interface IDriver { 
  //车辆型号 
  public void setCar(ICar car);
 //是司机就应该会驾驶汽车
  public void drive(); 
}

public class Driver implements IDriver{
  private ICar car; 
  public void setCar(ICar car){ 
  this.car = car; 
  }

 //司机的主要职责就是驾驶汽车
 public void drive(){ 
   this.car.run();
  } 

}


3、接口声明依赖对象

在接口的方法中声明依赖对象,未修改的IDriver和Driver就采用了接口声明依赖的方式,该方法也叫做接口注入。

public interface IDriver { 
 //老司机,会开车
  public void drive(ICar car); 
}

public class Driver implements IDriver{
   //司机的主要职责就是驾驶汽车 
   public void drive(ICar car){ 
     car.run(); 
   } 
}




⇐⇐ 设计模式—— 二:里氏替换原则



参考:

【1】:《设计模式之禅》
【2】:谈一谈依赖倒置原则
【3】:设计模式六大原则(3):依赖倒置原则
【4】:聊聊设计模式原则(三) -- 依赖倒置原则

Guess you like

Origin www.cnblogs.com/three-fighter/p/12347404.html