参考慕课网视频《Java设计模式精讲》:
视频作者:Geely
视频链接:https://coding.imooc.com/lesson/270.html#mid=17149
定义
高层模块不应该依赖低层模块,二者都应该依赖其抽象。
强调
抽象不应该依赖细节,细节应该依赖抽象。
针对接口编程,不要针对实现编程。
优点
可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险。
案例:清风先生通过看Java视频来提升自己。
public class JavaVideo {
//播放的功能
public void play(){
System.out.println("看java的视频");
}
}
public class MrQingFeng {
//看视频
public void watch(JavaVideo video){
video.play();
}
}
清风先生具有看的功能,并且依赖于JavaVideo这个类。接下来写测试类。
public class MainTest {
@Test
public void testMethod1(){
MrQingFeng mrQingFeng = new MrQingFeng();
JavaVideo video = new JavaVideo();
mrQingFeng.watch(video);
}
}
这段代码很简单,结果就是:清风先生通过看Java视频,成功的提升了自己的Java水平。但是,技术更新迭代很快,现在已经是人工智能和大数据的时代了。所以,清风先生想要开始学习Python这门语言。他还是更加倾向于视频学习。所以,清风先生想要看Python视频。但是呢,清风先生家里的播放器只支持Java格式的视频。不支持Python格式的。所以,没办法,他就放弃了。
上面的例子,就说明了,清风这个类是个高层模块,Java视频是一个低层模块。他依赖于具体的实现。如果需要看HTML、GO或者其他的视频,就要更改他所依赖的具体实现。这样耦合度很大,生产力很低,当需求变更的时候,就要面临大面积的重构。
所以,下面通过抽象,来解决这个问题。
案例:清风先生想要学习一门编程语言,大家都推荐他通过看书来学习,但是他却不知道应该买什么书。所以呢,我们就来帮帮他。
定义一个买书的接口:
package com.qfcwx.dependencyinversion;
/**
* @ClassName: Book
* @Author: 清风一阵吹我心
* @Description: TODO 一个买书的接口
* @Date: 2019/4/7 17:30
* @Version 1.0
**/
public interface Book {
/**
* 买书
*/
void buyBook();
}
他在B公司的朋友,推荐他买Java的书来学习,因为Java的生态圈已经很完善了。所以,就要定一个类来实现上面的接口。
public class JavaBook implements Book {
@Override
public void buyBook() {
System.out.println("买了一本java语言的书");
}
}
而他在A公司的朋友说现在容器化技术,区块链这么火。所以推荐他去学习GO语言,他又计划买GO语言的书。
public class GoBook implements Book {
@Override
public void buyBook() {
System.out.println("买了一本go语言的书");
}
}
人生苦短,我用Python。谁知道,在T公司的女朋友来信了,说现在Python这么火,不选择它,还去选别的,是不是今晚想跪键盘了?
面对三方面的支援,清风先生可谓举棋不定啊。没关系,我们来帮他定。
public class MrQingFeng {
private Book book;
public void setBook(Book book) {
this.book = book;
}
//买各种书的方法
public void buyKindOfBook() {
book.buyBook();
}
}
想成为一名程序猿,成为一名优秀的程序猿,光掌握一本语言是不够的,要养成终身学习的习惯。所以,建议清风先生,把每种语言的书都买上,这样也能解决你现在的纠结情绪,还不用跪键盘了。岂不是美哉?
public class MainTest {
public static void main(String[] args) {
MrQingFeng mrQingFeng = new MrQingFeng();
mrQingFeng.setBook(new JavaBook());
mrQingFeng.buyKindOfBook();
mrQingFeng.setBook(new PythonBook());
mrQingFeng.buyKindOfBook();
mrQingFeng.setBook(new GoBook());
mrQingFeng.buyKindOfBook();
}
}
看吧,通过依赖抽象的方式,帮清风先生解决了买书的困难。瞬间觉得自己很伟大呢。
言归正传,上面的例子,就是强调,我们要面向接口编程,不要针对其实现编程。若想扩展具体的实现,只需要实现接口,或者继承其抽象即可。
例如:清风先生又想买Netty、SpringBoot的书。当然没问题,只需要实现买书的接口就行了。不会对应用的整体架构造成影响。
知识扩展
因为清风先生依赖书这个接口,所以,需要将其注入。这里的注入方式,使用的是Setter方式。
当然还有构造器注入、接口方法注入。
构造器注入
private Book book;
public MrQingFeng(Book book){
this.book = book;
}
public void buyKindOfBook() {
book.buyBook();
}
接口方法
public void buyKindOfBook(Book book) {
book.buyBook();
}
上面说了三种接口注入的方式,在Spring中最常用的就是Setter和构造器注入,也推荐大家使用这两种方式。
即使想活成强人,也得一步步来不是,别太苦了自己,负面情绪多了就找人宣泄一下,青春加油,未来加油!