zoukankan      html  css  js  c++  java
  • 设计模式——三:依赖倒置原则


    @



    什么是依赖倒置原则?


    先看看依赖倒置原则的原始定义:

    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

    翻译一下,包含三层定义:

    ● 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
    ● 抽象不应该依赖细节;
    ● 细节应该依赖抽象。

    高层模块和低层模块容易理解,每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。

    抽象和细节呢?在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点就是可以直接被实例化。

    依赖倒置原则在Java语言中的表现就是:
    ● 模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过 接口或抽象类产生的;
    ● 接口或抽象类不依赖于实现类;
    ● 实现类依赖接口或抽象类。


    为什么要用依赖倒置原则?

    采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。

    以司机驾驶奔驰车为例:


    3-1:司机驾驶奔驰车类图

    在这里插入图片描述

    奔驰车提供一个方法run,代表车辆运行。
    奔驰车类:

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

    司机通过调用奔驰车的run方法开动奔驰车.
    司机类:

    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】:聊聊设计模式原则(三) -- 依赖倒置原则

  • 相关阅读:
    通过set和waitOne来控制子线程的运行和停止
    一种控制线程运行和停止的方法
    C# 运用params修饰符来实现变长参数传递
    ref和out的区别
    C# 抽象类和接口的区别
    更有效率的使用Visual Studio(一)
    更有效率的使用Visual Studio(二)
    RS232串口用事件接受数据(一问一答)
    socket的异步回调函数,采用一问一答
    js去重方法
  • 原文地址:https://www.cnblogs.com/three-fighter/p/12347404.html
Copyright © 2011-2022 走看看