面向对象设计原则之依赖倒转原则

一、定义

依赖倒转原则(Dependence Inversion Principle, DIP)的定义:高层模块不应该依赖于低层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

另一种表述:要针对接口编程,不要针对实现编程。

对各种概念进行一个描述:

  • 低层模块:不可分割的原子逻辑,可能会根据业务逻辑经常变化。
  • 高层模块:低层模块的再组合,对低层模块的抽象。
  • 抽象: 接口或抽象类(是底层模块的抽象,特点:不能直接被实例化)
  • 与接口或抽象类对应的实现类:低层模块的具体实现(特点:可以直拉被实例化)

依赖倒置原则的优点:

  • 可以通过抽象使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合(也是本质)
  • 可以规避一些非技术因素引起的问题(项目大时,需求变化的概率也越大,通过采用依赖倒置原则设计的接口或抽象类对实现类进行约束,可以减少需求变化引起的工作量剧增情况。同时,发生人员变动,只要文档完善,也可让维护人员轻松地扩展和维护)
  • 可以促进并行开发(如,两个类之间有依赖关系,只要制定出两者之间的接口(或抽象类)就可以独立开发了,规范已经定好了,而且项目之间的单元测试也可以独立地运行,而TDD开发模式更是DIP的最高级应用(特别适合项目人员整体水平较低时使用))

(参考自设计模式六大原则例子(四)-- 依赖倒置原则(DIP)例子

二、分析

依赖倒转原则就是指:代码要依赖于抽象的类,而不依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。也就是说,在程序代码中传递参数时或在组合聚合关系中,尽量引用层次高的抽象类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口和抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。也就是说要推迟对具体类的定义,尽量在代码中针对抽象编程,这样有助于设计出能够快速作出变更的解决方案,以便应对项目需求的变化。

下面介绍一下依赖倒转原则中经常提到的两个概念——类之间的耦合和依赖注入。

1.类之间的耦合

在面向对象系统中,两个类之间通常可以发生三种不同的耦合关系(依赖关系):

(1)零耦合关系:如果两个类之间没有任何耦合关系,称之为零耦合

(2)具体耦合关系:具体耦合发生在两个具体类中(可实例化的类)之间,由一个类对另一个具体类实例的直接引用产生

(3)抽象耦合关系:抽象耦合关系发生在一个具体类和一个抽象类之间,也可以发生在两个抽象类之间,使两个发生关系的类之间存在最大的灵活性。由于在抽象耦合中至少有一端是抽象的,因此可以通过不同的具体实现来进行扩展

依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。由于一个抽象耦合关系总要涉及具体类从抽象类继承,并且需要保证在任何引用到基类的地方都可以替换成其子类,因此,里氏代换原则是依赖倒转原则的基础。

2.依赖注入

依赖注入就是将一个类的对象传入另一个类,注入时应该尽量注入父类对象,而在程序运行时再通过子类对象来覆盖父类对象,依赖注入有以下三种方式:

(1)构造注入(Constructor Injection)

通过构造函数注入实例变量

public interface AbstractBook {
    public void view();
}

public interface AbstractReader {
    public void read();
}

public class ConcreteBook implements AbstractBook {
    public void view() {
        ......
    }
}

public class ConcreteReader implements AbstractReader {
    private AbstractBook book;
    public ConcreteReader(AbstractBook book) {
        this.book = book;
    }

    public void read() {
        book.view();
    }
}

 (2)设值注入(Setter Injection)

通过Setter方法注入实例变量

public interface AbstractBook {
    public void view();
}

public interface AbstractReader {
    public void setBook(AbstractBook book);
    public void read();
}

public class ConcreteBook implements AbstractBook {
    public void view() {
        ......
    }
}

public class ConcreteReader implements AbstractReader {
    private AbstractBook book;

    public void setBook(AbstractBook book) {
        this.book = book;
    }
    
    public void read() {
        boook.view();
    }
}

(3)接口注入(Interface Injection)

通过接口方法注入实例变量

public interface AbstractBook {
    public void view();
}

public interface AbstractReader {
    public void read(AbstractBook book);
}

public class ConcreteBook implements AbstractBook {
    public void view() {
        ......
    }
}

public class ConcreteReader implements AbstractReader {
    public void read(AbstractBook book) {
        book.view();
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值