1. 抽象不依赖细节,细节应该依赖抽象
  2. 程序应该依赖接口,而不应该依赖具体实现
  3. 简单来说,就是要求对抽象编程,而不要对实现编程
  4. 依赖倒转是面向过程程序设计的标志,考虑的是针对抽象编程而不是针对细节编程,即所有的依赖关系都终止与接口或者抽象,反之就是面向过程编程

原则

  1. 高层模块不应该依赖底层模块,两者都应该依赖抽象
  2. 抽象不应该依赖细节,细节应该依赖抽象

高层依赖底层的后果

  1. 高层依赖底层,存在比较高的耦合,如果底层发生更改会直接影响到高层,而高层与其他逻辑管理,可能会造成其他影响

举例:

  1. 比如计算机的主板 和 其他配件 的 关系
  2. 高层依赖底层,即 主板上的插槽限制必须为某种类型的配件 比如内存限制为金士顿的,那么只能使用金士顿的内存条,一旦需要换其他品牌的主板,则需要更换主板或者处理相应模

依赖倒转的好处

高层依赖抽象层,底层依赖抽象层,即使底层发生更改,也不会影响到高层

举例:

  1. 比如计算机的主板 和 其他配件 的 关系
  2. PC是易插拔模式,即主板不限制具体的配件品牌,只提供具体插槽(接口)供配件使用,
  3. 替换不同品牌的内存条,不会对主板造成影响,计算机能正常使用
  4. 主板 -- 接口 -- (配件1,配件2,...)
  5. 高层 --> 接口 <-- 底层

    修收音机 vs 修电脑
    困难 简单
    上下依赖,耦合过度 依赖倒转,易插拔

abstract class Disk{ }
abstract class Memory{ }

class Adisk : Disk{ }
class Bdisk: Disk{ }
class Amemory: Memory{ }

//上层依赖下层
class PCv1
{
   public Adisk adisk {get;set;}
   public Amemory aMemory {get;set;}
}

//上层依赖抽象
class PCv2
{
    public Disk disk {get;set;}
    public Memory Memory {get;set;}
}

void Main()
{
    //上层依赖下层
    PCv1 pv1 = new PCv1();
    pv1.adisk = new Bdisk(); //error, 只能使用 Adisk

    //依赖倒转

   PCv2 pv2 = new PCv2();
   pv1.disk = new Bdisk(); // 只要是继承 Disk 的底层都能使用
}

使用原则

  1. 每个类要尽量有接口或者抽象类,或者都有,依赖倒转基本要求:有了抽象才能依赖倒转
  2. 变量的表面类型尽量是接口或者抽象类型,这么做的目的是,可以尽可能替换多的底层使用
  3. 任务类都不应该从具体的类派生
  4. 尽量不要重写基类已经写好的方法(里氏替换原则,由于子类的可替换让使用父类类型的模块在无需修改的情况下就可以扩展)
  5. 结合里氏替换原则,interface 接口负责定义公共属性和方法,以及与其他对象的依赖关系
    abstract 抽象类 负责公共构造的部分,实现类的准确业务逻辑,同时对适当对父类进行细化