zoukankan      html  css  js  c++  java
  • 六大设计原则(三)DIP依赖倒置原则

    依赖倒置原则DIP(Dependence Inversion Principle)

    依赖倒置原则的含义

    • 高层模块不能依赖低层模块,二者都应该依赖其抽象。
    • 抽象不应该依赖于细节。
    • 细节应该依赖抽象。

    什么是高层模块?低层模块
    每一个原子逻辑就是低层模块,原子逻辑再组就是高层模块。
    什么是抽象和细节
    抽象是抽象类,不可被实例化。
    细节是实现类,比如实现的接口或继承抽象类的子类,可以被实例化。

    表现在Java语言中就是面向接口编程

    • 模块间的依赖是通过抽象来实现的,具体的实现类之间不能发生直接的依赖。
    • 接口或抽象类不能依赖与实现类。
    • 实现类依赖接口或抽象类。

    我们假设有三个类,一个为场景类,一个为司机类,一个为奔驰类。通过这三个类我们便可以实现司机开动汽车这个场景。如图
    司机驾驶奔驰车类图
    具体的实现代码如下
    司机类

    package des.DIP;
    //司机类
    public class Driver {
        //司机驾驶车 紧耦合
        public void drive(Benze benze){
            benze.run();
        }
        //司机驾驶宝马车 紧耦合
        public void drive(BMW bmw){
            bmw.run();
        }
    }
    
    

    奔驰类

    package des.DIP;
    //奔驰车
    public class Benze {
        public void run(){
            System.out.print("奔驰车开始运行...");
        }
    }
    
    

    场景类

    package des.DIP;
    //场景类
    public class Client {
        public static void main(String[] args){
            //创建一个司机
            Driver zs = new Driver();
            //创建一个奔驰车
            Benze benze = new Benze();
            //司机可以开奔驰车
            zs.drive(benze);
            //假设此时增加一个宝马车呢?还要再增加一个方法,并且重新创建
            //一个还好若是很多呢?难道要在司机类声明很多方法吗?
            BMW bmw = new BMW();
    
        }
    
    }
    
    
    package des.DIP;
    //宝马车
    public class BMW {
        //宝马车当然也可以开动
        public  void run(){
            System.out.print("宝马车开动...");
        }
    }
    
    

    程序正常的写法就是如此,但是如果我们考虑下面一个问题,司机并不是只会开着一辆Benze牌的车,假如我们再假如一个BMW(宝马)牌的车,我们传统的做法就是再新建一个类,然后再司机类中再添加一个drive BMW的方法。假如我们要添加无数品牌的汽车呢,难道还要再司机类中添加无数的drive方法吗?他们都有着相同的方法名,只是传入的汽车型号不同。
    显然,传统的drive方法的写法,具有紧耦合性,只要车型变更,就不能再使用了。其导致的结果就是系统的可维护性大大降低,可读性也大大降低


    解决方法
    使用依赖倒置原则

    DIP第一种方法 接口注入法

    建立两个接口,IDriver和ICar
    file
    此时业务的场景类就可以改写成如下

    package des.DIP;
    
    public class Client1 {
        public static void main(String[] args){
            //创建一个司机
            /**
             * 此处明确两个概念:
             * IDriver 叫做表面类型, Driver1 叫做实际类型  或称抽象类型和实际类型
             *
             * 此后所有的操作均是对抽象接口的操作,具体屏蔽了细节
             */
            IDriver ds = new Driver1();
            ICar c = new Bmw1();
            ds.drive(c);
    
    
        }
    }
    

    表面类型和实际类型: IDriver 叫做表面类型, Driver1 叫做实际类型 或称抽象类型和实际类型

    下面是接口类和实现类参考代码:

    package des.DIP;
    //司机接口
    public interface IDriver {
        //司机可以驾驶汽车,什么汽车不用管即抽象类(松耦合)
        public void drive(ICar car);
    }
    
    
    package des.DIP;
    //抽象汽车类
    public interface ICar {
        //汽车启动
        public void run();
    }
    
    package des.DIP;
    
    public class Driver1 implements  IDriver {
        @Override
        public void drive(ICar car) {
            car.run();
        }
    }
    
    package des.DIP;
    
    public class Bmw1 implements  ICar {
        @Override
        public void run() {
            System.out.print("宝马车开始运行...");
        }
    }
    
    
    package des.DIP;
    
    public class Benze1 implements  ICar {
        @Override
        public void run() {
            System.out.print("奔驰车开始运行...");
        }
    }
    
    

    假设我们项目中有两个类是依赖关系,此时我们只需要定义两个抽象类就可以独立开发了。

    DIP第二种方法 构造函数传递依赖对象

    package des.DIP;
    //司机接口
    public interface IDriver {
        //司机可以驾驶汽车,什么汽车不用管即抽象类(松耦合)
        public void drive(ICar car);
        /***************************/
        public void drive();
    }
    
    package des.DIP;
    
    public class Driver1 implements  IDriver {
        /******************************************************/
        private ICar car;
        //构造函数注入
        public Driver1(ICar _car){
            this.car = _car;
        }
        @Override
        public void drive() {
            this.car.run();
        }
        /******************************************************/
        @Override
        public void drive(ICar car) {
            car.run();
        }
    
        
    }
    
    
    IDriver ds1 = new Driver1(new Bmw1());
    ds.run();
    

    运行结果
    file
    构造函数依赖注入理解图示
    file
    file
    file

    DIP第三种方法 setter方法传递依赖对象

    file
    file
    file
    代码参考

    package des.DIP;
    //司机接口
    public interface IDriver {
        public void setCar(ICar car);
        public void drive();
    
    }
    
    package des.DIP;
    public class Driver1 implements  IDriver {
        /******************************************************/
        private ICar car;
    	    @Override
        public void setCar(ICar car) {
            this.car.run();
        }
      
        @Override
        public void drive() {
            this.car.run();
        }
    
    }
    
    
    package des.DIP;
    
    public class Client1 {
        public static void main(String[] args){
         
           IDriver ds1 = new Driver1();
            ds1.setCar(new Bmw1());
            ds1.drive();
    
    
        }
    }
    
    

    DIP总结

    • DIP本质就是通过抽象类来实现彼此独立,互不影响
    • 依赖倒置的核心是面向接口编程,即上面的第一种方法。
    • 依赖倒置的具体使用规则如下
      • 每个类尽量有接口或抽象类,或者二者都有。
      • 变量的表面类型尽量是接口或抽象类。
      • 任何类不应该从具体类派生。
      • 尽量不要覆写基类的方法。
      • 结合里氏替换原则进行。
    • 依赖倒置需要审时度势,而不是永远抓住这个原则不放,任何一个原则的优点都是有限的。

    对于倒置的理解

    从反面讲:什么是正置?如上例子,我们开什么型号的车,就依赖什么样型号的车。不存在什么抽象类与接口,直接单独建立即可,需要什么建立什么。但是依赖倒置?就是对车进行抽象,抽象出类和接口,建立抽象间的依赖。

  • 相关阅读:
    第51月第8天 nsdata分割
    第50月第25天 TestFlight不可用
    第50月第16日 licode libnice owt
    第50月第13天 webrtc抗丢包与带宽自适应
    第50月第12天 LiveVideoStack
    第50月第3天 webrtc fec
    第49月第29天 WebRTC GN构建工具的使用
    第49月第20天 在Mac OS X上.dylib和.so之间的区别
    iOS开发总结(A0)- make table view more responsive
    iOS开发总结(A0)- block基础知识
  • 原文地址:https://www.cnblogs.com/quinntian/p/10739188.html
Copyright © 2011-2022 走看看