DIP强调在应用中,高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。源代码依赖方向与控制流方向的反转,这是DIP被称为依赖反转的原因。DIP指导跨越架构边界。
DIP特点
- 降低耦合度:高层模块和低层模块之间的依赖关系通过抽象接口来实现,降低了直接依赖关系。
- 提高扩展性:低层模块的实现可以灵活替换,只要遵循抽象接口的规范即可。
- 便于测试:高层模块可以通过依赖注入等方式使用模拟对象(Mock)进行测试,而不依赖低层模块。
示例代码
不符合DIP的代码
class EmailSender {
public void send(String message) {
// 具体的邮件发送逻辑
}
}
class MessageService {
private EmailSender emailSender;
public MessageService() {
this.emailSender = new EmailSender();
}
public void sendMessage(String message) {
emailSender.send(message);
}
}
使用DIP优化后的代码
// 抽象接口
interface MessageSender {
void send(String message);
}
// 具体实现
class EmailSender implements MessageSender {
@Override
public void send(String message) {
// 具体的邮件发送逻辑
}
}
class SmsSender implements MessageSender {
@Override
public void send(String message) {
// 具体的短信发送逻辑
}
}
// 高层模块
class MessageService {
private MessageSender messageSender;
public MessageService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void sendMessage(String message) {
messageSender.send(message);
}
}
注意点
抽象优先
在设计初期定义抽象接口,避免先实现具体类再抽取接口。
使用依赖注入(DI)
通过构造器、Setter 方法或依赖注入框架(如 Spring)注入依赖。
避免在高层模块中实例化具体类
将对象创建逻辑封装在工厂类或配置文件中。
依赖注入框架的应用
在大型项目中使用 DI 框架(如 Spring)管理对象依赖关系。
不要在具体实现类上创建衍生类。
不要覆盖包含具体实现的函数。
避免在代码中写入任何具体实现相关的名字,或者是其他容易变动的事物的名字。
参考
《架构整洁之道》-- Robert C.Mattin
《架构师的自我修炼》 – 李智慧
《设计模式之美》 – 王争