设计原则之依赖倒置原则(DIP)

简介

依赖倒置原则是系统解耦的重要原则,遵循它可让咱们的系统更加健壮。java

定义

依赖倒置原则(Dependency Inversion Principle)是 Robert C. Martin 提出的,原则声明了两个方面:算法

  1. 上层模块不该该依赖下层模块,双方都应依赖于抽象。
  2. 抽象不该依赖实现,实现应该依赖抽象。

依赖倒置原则的声明中有几个概念:上层、下层,抽象、实现。ui

上层、下层是一类概念,在计算机的设计中,分层是常见的任务分解方法,每一层都使用下层提供的功能,又为更上层提供本身的功能。依赖倒置原则要求在设计层间通讯、交互标准时,不该依赖于某个下层,而是应该依赖于抽象,这样上下层之间就没有强耦合,若是两个实现都遵循了一样的抽象,则能够在上层无感知的状况下替换下层实现。设计

抽象、实现是一类概念,抽象是对同一类任务本质属性的表达,实现则是具体每一类任务的细节的表达。依赖倒置原则说明实现应该依赖于抽象,是由于实现是对抽象骨架的填充,而抽象不该依赖于实现,是由于抽象是对本质的概括,应去掉细节的干扰。code

看了上面的规则,还须要问一下依赖倒置原则倒置了什么呢?排序

从字面看依赖倒置固然是倒置了依赖,但核心是控制权的反转。咱们从下面的例子来解释。接口

实践

需求要求实现一个排序系统,系统须要实现各类排序算法,使用方能够根据须要调用不一样的排序算法来对本身的数据进行排序。ip

设计的接口以下:ci

public interface Sort {
    public void sort(int[] nums);
}

有不一样的排序算法实现:get

public class QuickSort {
    public void sort(int[] nums) {
        //quick sort implementation
        ...
    }
}

public class MergeSort {
    public void sort(int[] nums) {
        //merge sort implementation
        ...
    }
}

public class BubbleSort {
    public void sort(int[] nums) {
        //bubble sort implementation
        ...
    }
}
......

使用方使用:

public class Client {
    public static void main(String[] args) {
        Sort sort = new QuickSort();
        int[] nums = new int[10];
        // initial nums
        sort.sort(nums);
    }
}

能够看到,使用方在使用的时候依赖的是抽象的Sort 接口,可是接口是无法实例化的,所以第一句 Sort sort = new QuickSort(); 将实现实例化后赋值给变量,这里,使用方做为上层模块就依赖了下层实现,违反了依赖倒置原则。

要解决这个问题,须要将实例化的过程迁移到排序系统中,使用方经过配置、参数等方式选择本身要使用的算法,这样使用方就不依赖排序系统具体的实现,而只依赖于Sort 的接口抽象。

咱们实现一种由排序系统智能判断返回排序算法的简单工厂。

public final class SortFactory {
    private static final int SIZE_THRESHOLD = 300;
    public static Sort choose (int sortSize) {
        if (sortSize < SIZE_THRESHOLD) {
            return new BubbleSort();
        }
        
        return new QuickSort();
    }
}

使用方在在使用时直接使用 Sort sort = SortFactory.choose(size) 的形式来得到排序实例便可。

从这个例子中,咱们能够看到,实例化的控制权本来是在使用方的手中,但这样就将抽象与实现耦合在了一块儿,后面咱们使用简单工厂模式将控制权交回到排序系统,使用方就只须要调用工厂方法来获取实例便可而无需关心具体实现了。

总结

在考虑依赖倒置原则的使用时,跟单一职责原则同样,须要注意使用的粒度。若是全部的代码都符合依赖倒置原则,那就过犹不及了。

依赖倒置原则要求系统创建在抽象的基石之上,而不是实现的浮土之上。需求的变化是迅速而猛烈的,相应的就要求实现也是随时随地变化的,而其中的本质抽象倒是相对不变的,如此系统就能够保持健壮,不会因外部纷扰左右摇摆。

Reference

  1. Dependency inversion principle
  2. 浅析依赖倒转、控制反转、IoC 容器、依赖注入。
  3. SOLID Design Principles Explained: Dependency Inversion Principle with Code Examples