一. 依靠倒置原则的定义
1.核心思想
依靠倒置原则(Dependence Inversion Principle,DIP)
*1.高层模块 不应该依赖 低层模块, 两者都要靠抽象;
*2.抽象 不应该依赖 细节, 细节 应该依赖 抽象; 接口和抽象价值在于设计;
*3.依赖倒转(倒置)的中心思想是面向接口编程,实现接口而不是继承或调用实现;
*4.依靠倒转原则的设计理念:
通过抽象发生模块间的依赖,实现类间无直接依赖关系,其依赖关系是通过接口或抽象类产生的;
相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础搭建的框架比以细节为基础
结构要稳定得多,在java抽象是指接口或抽象类(抽象战略方法),细节是具体的实现类(多变战术实现);
使用接口或抽象类的目的是制定规范(设计),而不涉及任何具体操作,将处理细节的任务交给子实现类;
2.注意事项和细节
1)注意事项
*1.低层模块应尽可能抽象或接口,或两者都有,程序稳定性更好;
*2.变量的声明类型应尽可能抽象或接口,以便在变量参考和实际对象之间存在缓冲层,有利于程序的扩展和优化;
*3.继承时遵循里氏替代原则;
2)注意细节
*1.高层模块不应依赖低层模块,两者都应依赖抽象;
*2.抽象不应依赖细节(具体实现类);
*3.细节(具体实现类)应依赖抽象(实现抽象);
二.依靠倒置原则的作用
1. 依靠倒置原理可以降低类间耦合。
2. 依靠倒置原则可以提高系统的稳定性。
3. 依靠倒置原则可以降低并行开发的风险。
4. 依靠倒置原则可以提高代码的可读性和可维护性。
三.依靠倒置实现模式
依靠倒置原则的目的是通过面向接口的编程来减少类之间的耦合,因此只要我们在实际编程中遵循以下四点,我们就可以在项目中满足这一规则。
1. 尽量提供接口或抽象类,或两者都有;
2. 变量声明类型尽量为接口或抽象类;
3. 任何类别都不应从具体类别中衍生;
4. 尽量遵循里氏替代原则,使用继承;
四.依赖关系传递的三种方式
*1.通过构造函数传输依赖对象:通过构造方法传输接口 (例如,构造函数的形参是抽象的或接口 Entry(Interface iView);)
*2.通过setter传递依赖对象的方法:通过setter方法传输接口实现类(setListener(Interface iView){this.iView=iView;} )
*3.接口声明实现依赖对象:通过方法直接传输接口
示例 IOpenAndClose 依赖 ITV
//TV接口 interface ITV{ fun play()} //实现类 class ChangHong : ITV { override fun play() { println("长虹电视机打开了") } } /** * 依赖关系传递的三种方式 * 1.基于接口传输 : 接口回调传值 * 2.基于结构传输方法 * 3.setter方式传递 */ //方法1:依赖于接口传输 fun func1(){ val changHong:ChangHong= ChangHong() val openAndClose:OpenAndClose= OpenAndClose() openAndClose.open(changHong) } interface IOpenAndClose{ fun open(tv:ITV)//抽象方法,接收接口 } //实现类,通过传输接口实现类调用接口法 class OpenAndClose : IOpenAndClose{ override fun open(tv: ITV) { tv.play() } } //方式2 通过构造方法依赖传递 fun func1(){ val changHong:ChangHong= ChangHong() val openAndClose: OpenAndClose = OpenAndClose(changHong) openAndClose.open() } interface IOpenAndClose{ fun open()//抽象方法 } ///通过实现构造器传输接口 依赖 class OpenAndClose(tv:ITV) : IOpenAndClose{ private val mTv: ITV = tv override fun open() { mTv.play() } } /方式3:通过Setter方法 依赖 fun func3(){ val changHong: ChangHong = ChangHong() val openAndClose: OpenAndClose = OpenAndClose() openAndClose.setTv(changHong) openAndClose.open() } interface IOpenAndClose { fun open()//抽象方法 fun setTv(tv: ITV) } //通过实现类调用setter传递依赖的方法 class OpenAndClose : IOpenAndClose { private var mTv: ITV? = null override fun open() { mTv? = null override fun open() { mTv?.play() } override fun setTv(tv: ITV) { mTv = tv } }
五.实例
让细节的具体实现类依赖于(使用)抽象类或接口,而不是让接口或抽象类依赖于细节的具体实现;在接收信息功能程序中依赖倒置原则的应用:
1.类图

2.代码示例
object DependenceInversion { @JvmStatic fun main(args: Array<String>) { val person = Person() person.receive(Email()) person.receive(WeChat()) } } /** * 需求: 完成Person接收信息的功能 * 方式1分析 * 1.简单,通过建立具体的新闻类别,比如: Email WeChat Sms转移自己的getInfo方法 * 2.问题:这种实现会因需求而增加新的类别,person还应增加相应的接收方法 * 3.解决方案:引入抽象界面IReceiver,表示接收者,这样Person类与接口IReceiver建立依赖 * 4.因为Email,WeChat 等都属于IReceiver他们各自实现了接收范围IReceiver接口,这样,我们就符合依赖倒置的原则 * @author xuezhihui * @date 2020/11/23 20:03 */ open class Person { //这里Person依赖接口, 传参是接口的实现类 fun receive(receiver: IReceiver?) { println(receiver?) { println(receiver?.getInfo()) } } ///信息接收器 interface IReceiver { fun getInfo(): String? } //接收E-mail信息 class Email :IReceiver { override fun getInfo(): String? { return "Email接收的信息" } } ///接收微信信息 class WeChat :IReceiver { override fun getInfo(): String? { return "WeChat接收的信息" } }
程序的运行结果如下:
Email接收的信息 WeChat接收的信息