依赖倒置原则
【 概念 】
“
面向接口编程
”
依赖倒转原则是基于这样的设计理念:
相对于细节的多变性,抽象的东西要稳定的多。
以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。
在java中,抽象指的是接口或抽象类,细节就是具体的实现类
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
【 特点 】
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象 (接口 或 抽象类)
- 依赖倒转(倒置)的中心思想是面向接口编程
【 场景介绍 】
Person类中 存在一个 接受消息(可能是任意消息)方法 (怎样实现?)
【 违反–依赖倒置原则 】
在该例中:
Person具体依赖于Email实现类
如果此时Person还需要接收类似 微信 QQ发送的消息就意味着要多个接收方法的实现
public class Inversion {
//客户端
public static void main(String[] args) {
Person person = new Person();
person.receiveEmail(new Email("发送了一条消息:Hello World!"));
person.receiveWeChat(new WeChat("发送了一条消息:Hello World!"));
person.receiveTencent(new Tencent("发送了一条消息:Hello World!"));
}
}
//Person类 接受消息功能
//方式 1 依赖具体实现类
class Person{
public void receiveEmail(Email email){
System.out.println(email.getInfo());
}
public void receiveWeChat(WeChat wechat){
System.out.println(wechat.getInfo());
}
public void receiveTencent(Tencent tencent){
System.out.println(tencent.getInfo());
}
}
//邮件类
class Email{
String info;
Email(String info){
this.info = "Email"+info;
}
//获取邮件信息
public String getInfo(){
return this.info;
}
}
//微信类
class WeChat{
String info;
WeChat(String info){
this.info = "Wechat"+info;
}
//获取微信信息
public String getInfo(){
return this.info;
}
}
//QQ类
class Tencent {
String info;
Tencent(String info){
this.info = "QQ"+info;
}
//获取微信信息
public String getInfo(){
return this.info;
}
}
【 运行结果 】
虽然实现了功能
但是导致重复工作(多个功能类似的方法 receive Email、WeChat、Tencent等)切不易维护
【 遵循–依赖倒置原则 】
优化方法:
引入一个IReceiver接口
Person类与这个接口发生依赖
具体发送消息类(实现类)这只需依赖该 实现该接口即可
public class Inversion {
//客户端
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email("发送了一条消息:Hello World!"));
person.receive(new WeChat("发送了一条消息:Hello World!"));
person.receive(new Tencent("发送了一条消息:Hello World!"));
}
}
//Person类 接受消息功能
//方式 1 依赖具体实现类
class Person{
//Person类只需一个方法即可接受所有类型消息
public void receive(IReceiver receiver){
System.out.println(receiver.getInfo());
}
}
//抽象公共接口
interface IReceiver{
public String getInfo();
}
//邮件类
class Email implements IReceiver{
String info;
Email(String info){
this.info = "Email"+info;
}
//获取邮件信息
@Override
public String getInfo(){
return this.info;
}
}
//微信类
class WeChat implements IReceiver{
String info;
WeChat(String info){
this.info = "Wechat"+info;
}
//获取微信信息
@Override
public String getInfo(){
return this.info;
}
}
//QQ类
class Tencent implements IReceiver{
String info;
Tencent(String info){
this.info = "QQ"+info;
}
//获取微信信息
@Override
public String getInfo(){
return this.info;
}
}
【 运行结果 】
在优化后:
Person类需要一个receive方法即可接收所有类型的消息
在后期维护中 若某个消息功能不再使用 也只需要删除对应的实现类 而不用修改Person类
【 依赖方法的传递方式 】
- 接口传递
- 构造方式传递
- setter方法传递
【
构造方式传递
】–:即 Person通过构造方法直接传入实现类 方法内部具体通过传入的实现类直接实现
public static void main(String[] args) { Person personE = new Person(new Email("发送了一条消息:Hello World!")); personE.receive(); Person personW = new Person(new WeChat("发送了一条消息:Hello World!")); personW.receive(); Person personT = new Person(new Tencent("发送了一条消息:Hello World!")); personT.receive(); } } //Person类 接受消息功能 //方式 1 依赖具体实现类 class Person{ //抽象公共接口对象 IReceiver receiver; public Person(IReceiver receiver){ this.receiver = receiver; } //通过传入的实现类 直接使用 public void receive(){ System.out.println(this.receiver.getInfo()); } } //抽象公共接口 interface IReceiver{ public String getInfo(); } //邮件类 class Email implements IReceiver{ ... } //微信类 class WeChat implements IReceiver{ ... } //QQ类 class Tencent implements IReceiver{ ... }
可以看到 方法不再需要传递实现类 但是Person对象却在增多
【
setter方法传递
】public static void main(String[] args) { Person person = new Person(); person.setReceiver(new Email("发送了一条消息:Hello World!")); person.receive(); person.setReceiver(new WeChat("发送了一条消息:Hello World!")); person.receive(); person.setReceiver(new Tencent("发送了一条消息:Hello World!")); person.receive(); } //Person类 接受消息功能 //方式 1 依赖具体实现类 class Person{ //抽象公共接口对象 IReceiver receiver; //传入具体实现类对象 public void setReceiver(IReceiver receiver) { this.receiver = receiver; } public void receive(){ System.out.println(this.receiver.getInfo()); } } //抽象公共接口 interface IReceiver{ public String getInfo(); } //邮件类 class Email implements IReceiver{ ... } //微信类 class WeChat implements IReceiver{ ... } //QQ类 class Tencent implements IReceiver{ ... }
因为是非构造方式传递 所以只需一个person即可
但是每次接受不同消息时 必须每次通过setter方法重设实现对象(不设置会产生错误甚至报错)
【 注意事项及细节 】
- 低层模块 尽量都要有抽象类或接口 程序稳定性好 (尽量每个实现类上都有一个抽象接口或抽象类)
- 变量的声明类型尽量是抽象类或接口,这样变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
- 继承时遵循里氏替换原则
OBJ业务类直接依赖B接口/抽象类缓冲 具体实现类A依赖B接口/抽象类
在后期更改业务时 只需在B类缓冲层中 添加新的方法 即可所有实现类都可以实现升级扩展