认识六大设计原则-浅谈依赖倒置原则
写在前面,马克思提出过经济基础决定上层建筑,那么对于日常进行开发的我们,要有一个初步的认识,所谓的23种设计模式,其实就是上层建筑,那么它的经济基础是什么呢?在个人看来,23种设计模式的经济基础就是六大设计原则。正如链表和数组是经济基础,数据结构上层建筑一样。那么下面,让我们去认识下这六大设计原则。
六大设计原则:
- (1)单一指责原则
- (2)里氏替换原则
- (3)接口隔离原则
- (4)迪米特法则
- (5)开闭原则
- (6)依赖倒置原则
上面具体其他五种设计原则的解释,我们放在后面的文章中,一个个的去认识它们,今天我们来认识下依赖倒置原则,看下百度百科对依赖倒置设计原则的解释如下,依赖倒置原则有两种描述:
第一种表述是:细节应当依赖于抽象,抽象不应当依赖于细节。
第二种描述是:要针对接口编程,不要针对实现编程。意思就是应当使用接口和抽象类而不是具体类进行变量的类型声明、参数的类型声明、方法的返回类型声明以及数据类型的转换等。要保证这一点,一个具体java类应当只实现java接口和抽象java类中声明过的方法,而不应当给出多余的方法。
上述这两种解释如果不通俗易懂的话,那么换一种解释:
高层模块不应依赖于低层模块,二者应依赖于抽象。抽象不应依赖于细节,细节应依赖于抽象。
上面这个解释是不是感觉也是有点抽象呢?那么在我们实际的工作中,高层模块是什么?底层模块又是什么?抽象又是什么?细节又是什么?怎么没有体现出来“倒置”这个词呢?那么倒置又是什么呢?我们带着这些疑问往下看。
依赖倒置中的依赖是什么?
从我个人的这几天的认识和理解中,个人认为,要理解依赖倒置原则,我们需要把这四个字拆开来看,依赖和倒置 ,下面我们首先看依赖,再看倒置。
依赖
这个词很通俗易懂,从依赖这个词可以得到,只有发生关系或者可能发生关系的两种事物才会有依赖,即依赖方和被依赖方,从而两者之间导致形成一种依赖关系。那么我们再看这个依赖关系,依赖关系也分为强依赖和弱依赖,
这里所谓的强依赖指的是,依赖方的所要处理的事情,或者完成一件事情,必须依赖被依赖方,这种关系称之为强依赖,正如汽车想要启动,必须要有发动机一样,放在我们开发的角度,可以说没有jvm,你写的那些经过编译器形成的class字节码文件,仅仅就是个文件,并不能运行,那么此时。
那么什么叫做弱依赖呢,弱依赖就是在完成一件事情的过程中,可以不需要被依赖方的支持,比如你想要汽车启动,这个时候车载蓝牙可有可无,因为你启动不需要蓝牙,蓝牙只是你想在开车过程中,增加自己的驾驶愉悦感一样,放在我们平时开发的角度,可以这么说有些系统调用日志,你想输出就输出,你不输出它也不会影响服务的正常运行,你虽然依赖了日志服务,但是你可用可不用。
倒置
倒置 稍微有点文化的人,感觉都会被这个名词给迷惑,这里的迷惑是指,按照常规的解释,这不就是颠倒次序吗,难道被依赖方离开依赖方不能生存,正如张三的女朋友把张三劈腿了之后,她会很难过?明眼人都知道这个事情不可能。那么这里的倒置指的是什么呢?我咨询过带过我的师傅,以及自己也对这个词有自己的理解。对于倒置,这里给出两种看法:
第一种看法,我师傅的看法: 依赖双方所有权的变化层面,以前的所有权是依赖双方相互控制的,现在的所有权不再受对方限制,这种关系发生了变化。
第二种看法,我的理解:这种依赖方和被依赖方的依赖关系,是由外部来决定。
进一步理解依赖和倒置
通过对依赖和倒置做了一个简单的表述,这里给出一个例子进一步理解依赖倒置原则,看网上的博客,大家都喜欢贴点图和贴点代码,这里我也随波逐流一下。 在古代,通信方式没有这么发达的情况下,人靠马车这种交通工具来去送东西。
随着人类社会的发展,交通工具也越来越多,汽车、飞机、轮船,给我们的选择也越来越多,所以我们适应时代的发展,选择开车,或者选择其他的交通工具去送东西。但是老王除了骑马,其他的交通工具,他都不会,东西送的又比较远,这可真的是难死了。这个时候,老李给他支了一招,让老王去找快递公司,让快递公司去送。于是,关系演化成了如下这种关系:
这种关系的改变,是不是就论证了上面对于倒置的理解的说法。其中这个快递公司扮演一个外部角色,完成送这个东西到目的地,具体使用什么工具,由它来决定和适配,从而完成送东西这件事,改变了以前的那种强依赖关系,不再由依赖双方进行相互控制。
此处的快递公司还是对所有快递公司的一种抽象,因为有些快递公司不支持空运,有些快递公司支持空运,所以快递公司在这里就是对所有快递公司的一种抽象,即每个快递公司都可以邮寄东西,至于每个快递公司怎么邮寄东西的过程,这里称之为细节。高层模块和底层模块其实就是依赖方和被依赖方,这里就是人和运送工具,只是这个词语搞的比较抽象。下面给出示例代码如下:
public class Main {
public static void main(String[] args) {
//使用飞机运输东西
Plane plane = new Plane();
//快递公司决定使用那种交通工具
SendCompany sendCompany = new SendCompany(plane);
//人再也不受不会某种交通工具的限制
Person person = new Person(sendCompany);
//运输东西
person.sendThing();
}
}
public class Person {
private CourierCompany courierCompany;
public Person(CourierCompany courierCompany) {
this.courierCompany = courierCompany;
}
public void sendThing(){
courierCompany.sendThings();
}
}
public interface CourierCompany {
void sendThings();
}
public class SendCompany implements CourierCompany{
private SendTool sendTool;
public SendCompany(SendTool sendTool) {
this.sendTool = sendTool;
}
@Override
public void sendThings() {
sendTool.loadAndSend();
}
}
public interface SendTool {
void loadAndSend();
}
public class Plane implements SendTool{
@Override
public void loadAndSend() {
System.out.println("我拉的多,飞的还高");
}
}
当你不想使用飞机空运的时候,你随便扩展一个汽车运输,实现运输的接口,是不是就做到最小程度的代码侵入。
public class Car implements SendTool {
@Override
public void loadAndSend() {
System.out.println("送东西,还是开车方便");
}
}
public class Main {
public static void main(String[] args) {
//使用汽车运输
Car car = new Car();
//快递公司决定使用那种交通工具
SendCompany sendCompany = new SendCompany(car);
//人再也不受不会某种交通工具的限制
Person person = new Person(sendCompany);
//运输东西
person.sendThing();
}
}
遵循依赖倒置原则的好处
我们想一下,遵循这种开发原则的好处是什么?如果我们不遵循这种开发原则的坏处有哪些?首先从个人角度说下不遵循这种开发原则的坏处:
- 程序耦合度高
- 代码难以维护
为什么说耦合度高,代码难以维护呢?像之前写死在代码里面,依赖方依赖被依赖方的具体实现,那么当需求更改的时候,比如,之前使用汽车陆运,现在时间紧,需要转空运,这个时候,你肯定要去改源代码,修改依赖的具体实现。正如在我们日常开发过程中,在业务层,直接对数据进行操作,那么一旦业务逻辑发生一丁丁点变化,或者数据存储服务改变,例如从mysql存储切换到es存储,那么你是不是需要修改源代码。一旦代码发生变化,那么所有功能是不是又要来一遍测试(谁能百分百保证不出bug),想一下,类似这种操作,是不是又违反了开闭原则,对扩展开放,对修改关闭的原则。
好处是什么呢,好处就是坏处的对立面,哈哈哈。
写到最后,新人职场小白,第一次写文章,如有文中表述不对的地方,请各位大佬批评指正。