示例1:司机驾驶汽车
问题场景:司机类直接依赖奔驰车类,新增宝马车需修改司机类代码。
java
// 未遵循DIP
class Benz { public void run() { /*...*/ } }
class Driver {
public void drive(Benz benz) { benz.run(); }
}
// 遵循DIP:引入接口ICar
interface ICar { void run(); }
class Benz implements ICar { /*...*/ }
class BMW implements ICar { /*...*/ }
class Driver {
public void drive(ICar car) { car.run(); }
}
效果:新增汽车类型(如BMW
)无需修改Driver
类,仅需实现ICar
接口。
示例2:用户接收消息
问题场景:用户类直接依赖邮件类,新增微信或短信需修改用户类。
java
// 未遵循DIP
class Email { public String getInfo() { /*...*/ } }
class Person {
public void receive(Email email) { /*...*/ }
}
// 遵循DIP:引入接口IReceiver
interface IReceiver { String getInfo(); }
class Email implements IReceiver { /*...*/ }
class WeChat implements IReceiver { /*...*/ }
class Person {
public void receive(IReceiver receiver) { /*...*/ }
}
效果:新增消息类型(如WeChat
)无需修改Person
类,仅需实现IReceiver
接口。
示例3:购物场景
问题场景:顾客类直接依赖具体商品类(如汉堡、薯条),新增商品需修改顾客类。
java
// 未遵循DIP
class Hamburger { public void eat() { /*...*/ } }
class Person {
public void buy(Hamburger hamburger) { hamburger.eat(); }
}
// 遵循DIP:引入接口IDishes
interface IDishes { void eat(); }
class Hamburger implements IDishes { /*...*/ }
class Chips implements IDishes { /*...*/ }
class Person {
public void buy(IDishes dishes) { dishes.eat(); }
}
效果:新增商品类型(如Chips
)无需修改Person
类,仅需实现IDishes
接口。
示例4:通知服务
问题场景:通知服务直接依赖邮件发送类,更换短信服务需修改通知逻辑。
java
// 未遵循DIP
class EmailService { public void sendEmail(String msg) { /*...*/ } }
class NotificationService {
public void send(String msg) { new EmailService().sendEmail(msg); }
}
// 遵循DIP:引入接口MessageSender
interface MessageSender { void sendMessage(String msg); }
class EmailService implements MessageSender { /*...*/ }
class SMSService implements MessageSender { /*...*/ }
class NotificationService {
private MessageSender sender;
public NotificationService(MessageSender sender) {
this.sender = sender;
}
public void send(String msg) { sender.sendMessage(msg); }
}
效果:通过构造器注入依赖,更换消息服务(如SMSService
)仅需修改调用层。
依赖传递的三种方式
- 接口传递:通过方法参数传递抽象(如
Driver.drive(ICar car)
)。 - 构造器注入:通过构造函数依赖注入(如
NotificationService
依赖MessageSender
)。 - Setter注入:通过Setter方法动态注入依赖(如
public void setSender(MessageSender sender)
)。
DIP的核心优势
- 降低耦合性:模块间通过抽象交互,减少直接依赖。
- 提高扩展性:新增功能只需添加实现类,无需修改高层逻辑(符合开闭原则)。
- 支持并行开发:接口定义后,高低层模块可独立开发与测试(如TDD模式)。
- 增强可维护性:变更影响范围受限于低层模块,减少风险。
总结
依赖倒置原则通过抽象(接口/抽象类)实现模块解耦,核心实践包括:
- 定义清晰的接口或抽象类作为模块间的契约。
- 使用依赖注入(构造器、Setter等)传递具体实现。
- 高层模块仅依赖抽象,低层模块实现抽象。
结合Spring框架的DI容器,可进一步自动化依赖管理,提升代码复用性和灵活性。