目录
高层模块底层模块
一、定义与核心思想
- 基本定义
-
- 高层模块不应依赖低层模块,两者都应依赖抽象(接口或抽象类)。
- 抽象不应依赖细节,而细节(具体实现类)应依赖抽象。
- 核心目标
通过面向接口编程,打破传统“高层调用低层”的依赖关系,实现模块间解耦。例如:
-
- 传统依赖:司机类直接调用奔驰车类的方法,导致更换车型需修改司机代码。
- 倒置后:司机依赖“车辆接口”,奔驰车、宝马车均实现该接口,实现灵活扩展。
二、实现方式
- 定义抽象层
通过接口或抽象类规范模块间的交互契约。例如:
-
- 定义消息发送接口
MessageService
,由EmailService
或SmsService
实现具体逻辑。
- 定义消息发送接口
- 依赖注入(Dependency Injection)
将具体实现通过构造函数、属性或方法参数注入到高层模块,而非在内部直接实例化。例如:
public class NotificationService {
private final MessageService messageService;
// 通过构造函数注入依赖
public NotificationService(MessageService messageService) {
this.messageService = messageService;
}
}
- 避免直接引用具体类
高层模块仅通过抽象接口操作,如顾客购物场景中依赖抽象的Shop
接口,而非具体网店类。
三、优点与价值
- 降低耦合度
模块间通过抽象交互,减少直接依赖,修改低层实现时无需调整高层代码。 - 提高可扩展性
新增功能只需实现抽象接口,例如添加新车型或消息发送方式时,无需修改现有业务逻辑。 - 增强可测试性
通过模拟抽象接口的实现,便于单元测试(如使用Mock对象)。 - 支持并行开发
抽象接口定义后,高层与低层模块可独立开发。
四、典型应用场景
- 模块化系统设计
在分层架构中,业务逻辑层(高层)通过接口调用数据访问层(低层),避免数据库变更影响业务代码。 - 框架开发
如Spring框架通过依赖注入容器管理对象生命周期,实现组件解耦。 - 插件化扩展
系统通过抽象接口支持第三方插件,如IDE工具可通过接口集成不同编译器。
五、与其他原则的关系
- 开闭原则(OCP)的基础:依赖倒置通过抽象隔离变化,使系统易于扩展。
- 里氏替换原则(LSP)的延伸:抽象接口的稳定性确保子类可替换性。
示例说明
场景:顾客在不同网店购物
- 未遵循DIP:顾客类直接依赖韶关网店类,更换网店需修改顾客代码。
- 遵循DIP后:顾客依赖抽象的
Shop
接口,新增婺源网店只需实现该接口,无需修改顾客类。
// 抽象接口
public interface Shop {
String sell();
}
// 具体实现
public class WuyuanShop implements Shop {
public String sell() { return "绿茶"; }
}
// 高层模块
public class Customer {
public void shopping(Shop shop) {
System.out.println(shop.sell());
}
}
通过依赖倒置原则,系统从“细节驱动设计”转向“抽象驱动设计”,显著提升灵活性和可维护性。实际开发中需结合依赖注入框架(如Spring)和接口设计规范落地此原则。
依赖倒置原则
依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计的五大SOLID原则之一,其核心思想是通过抽象解耦模块间的直接依赖关系,提高系统的灵活性和可维护性。以下是关键要点解析:
一、定义与核心思想
- 基本定义
-
- 高层模块不应依赖低层模块,两者都应依赖抽象(接口或抽象类)。
- 抽象不应依赖细节,而细节(具体实现类)应依赖抽象。
- 核心目标
通过面向接口编程,打破传统“高层调用低层”的依赖关系,实现模块间解耦。例如:
-
- 传统依赖:司机类直接调用奔驰车类的方法,导致更换车型需修改司机代码。
- 倒置后:司机依赖“车辆接口”,奔驰车、宝马车均实现该接口,实现灵活扩展。
二、实现方式
- 定义抽象层
通过接口或抽象类规范模块间的交互契约。例如:
-
- 定义消息发送接口
MessageService
,由EmailService
或SmsService
实现具体逻辑。
- 定义消息发送接口
- 依赖注入(Dependency Injection)
将具体实现通过构造函数、属性或方法参数注入到高层模块,而非在内部直接实例化。例如:
public class NotificationService {
private final MessageService messageService;
// 通过构造函数注入依赖
public NotificationService(MessageService messageService) {
this.messageService = messageService;
}
}
- 避免直接引用具体类
高层模块仅通过抽象接口操作,如顾客购物场景中依赖抽象的Shop
接口,而非具体网店类。
三、优点与价值
- 降低耦合度
模块间通过抽象交互,减少直接依赖,修改低层实现时无需调整高层代码。 - 提高可扩展性
新增功能只需实现抽象接口,例如添加新车型或消息发送方式时,无需修改现有业务逻辑。 - 增强可测试性
通过模拟抽象接口的实现,便于单元测试(如使用Mock对象)。 - 支持并行开发
抽象接口定义后,高层与低层模块可独立开发。
四、典型应用场景
- 模块化系统设计
在分层架构中,业务逻辑层(高层)通过接口调用数据访问层(低层),避免数据库变更影响业务代码。 - 框架开发
如Spring框架通过依赖注入容器管理对象生命周期,实现组件解耦。 - 插件化扩展
系统通过抽象接口支持第三方插件,如IDE工具可通过接口集成不同编译器。
五、与其他原则的关系
- 开闭原则(OCP)的基础:依赖倒置通过抽象隔离变化,使系统易于扩展。
- 里氏替换原则(LSP)的延伸:抽象接口的稳定性确保子类可替换性。
示例说明
场景:顾客在不同网店购物
- 未遵循DIP:顾客类直接依赖韶关网店类,更换网店需修改顾客代码。
- 遵循DIP后:顾客依赖抽象的
Shop
接口,新增婺源网店只需实现该接口,无需修改顾客类。
// 抽象接口
public interface Shop {
String sell();
}
// 具体实现
public class WuyuanShop implements Shop {
public String sell() { return "绿茶"; }
}
// 高层模块
public class Customer {
public void shopping(Shop shop) {
System.out.println(shop.sell());
}
}
通过依赖倒置原则,系统从“细节驱动设计”转向“抽象驱动设计”,显著提升灵活性和可维护性。实际开发中需结合依赖注入框架(如Spring)和接口设计规范落地此原则。
自己理解
在依赖倒置原则中 高层模块不是调用底层模块
而是高层模块和底层模块都实现接口
底层模块实现接口
高层模块调用接口里面的方法
开闭原则
开闭原则(Open-Closed Principle, OCP)的基础是通过抽象化和多态性实现系统的可扩展性,其核心思想是在不修改已有代码的前提下扩展功能。以下是其具体基础要素:
1. 抽象化与接口设计
开闭原则的关键在于定义稳定的抽象层。通过接口或抽象类对功能进行抽象,将可变部分与不变部分分离,确保扩展时仅需新增实现而非修改原有结构。例如:
- 通过接口定义通用行为(如宠物管理系统的
Pet
接口),具体实现通过多态扩展新类型(猫、狗、鸟); - 使用抽象类(如
AbstractChart
)统一管理不同图表类型,新增图表仅需继承抽象类。
2. 封装与模块化
- 封装变化:将可能变化的部分独立封装(如价格计算逻辑),通过新增类或配置实现扩展,而非直接修改原有类。
- 模块化设计:系统划分为高内聚、低耦合的模块,每个模块职责单一,减少修改影响范围。
3. 多态性与继承机制
- 利用多态特性,通过子类化扩展功能。例如,新增
OffNovelBook
子类实现打折逻辑,而非修改原书籍类; - 结合策略模式、装饰器模式等设计模式,动态替换行为。
4. 依赖抽象而非具体实现
- 高层模块依赖接口或抽象类,而非具体实现类。例如,计算器类依赖
ArithmeticOperation
接口,支持灵活扩展加减乘除运算; - 遵循依赖倒置原则(DIP),确保系统核心逻辑与细节解耦。
5. 设计原则的协同作用
- 里氏替换原则(LSP):子类可替换父类,保障扩展的兼容性;
- 单一职责原则(SRP):每个类仅承担单一职责,降低修改风险。
总结
开闭原则的底层逻辑是通过抽象构建稳定框架,利用多态和封装实现灵活扩展。其成功应用依赖于合理的抽象设计、模块化架构以及与其他设计原则(如SOLID原则)的协同。这种设计模式显著提升了系统的可维护性、复用性和应对需求变化的敏捷性。
接口隔离原则(Interface Segregation Principle,ISP)是面向对象设计的SOLID原则之一,其核心思想是通过拆分臃肿的接口,减少类之间的不必要依赖,从而提高系统的灵活性和可维护性。以下是具体解析:
接口隔离原则
一、核心定义
- 基本概念
客户端(接口使用者)不应被迫依赖其不需要的接口方法。应将庞大接口拆分为多个小接口,每个接口专注于单一功能。 - 核心目标
-
- 降低耦合:通过细化接口,减少类对无关方法的依赖。
- 提高内聚:每个接口专注单一职责,避免冗余实现。
二、与单一职责原则的区别
- 单一职责原则(SRP)
关注类或接口的职责单一性,强调业务逻辑的划分。 - 接口隔离原则(ISP)
强调接口设计的最小化,要求方法尽可能少,避免客户端被迫实现无用方法。
示例:
一个电商系统的用户操作接口若包含登录、支付、订单管理等多个方法,违反ISP。应拆分为Loginable
、Payable
、OrderManageable
等独立接口,让不同模块按需实现。
三、应用场景与实现方式
- 典型场景
-
- 系统需要支持多种客户端(如移动端、PC端)访问不同功能。
- 接口方法存在冗余(如打印机同时支持打印、扫描、传真,但部分客户端仅需部分功能)。
- 实现方法
-
- 拆分接口:将大接口按功能拆分为多个小接口(如
Printer
、Scanner
、FaxMachine
)。 - 组合继承:通过接口继承组合所需功能,避免实现冗余方法。
- 拆分接口:将大接口按功能拆分为多个小接口(如
代码示例:
// 拆分前:臃肿接口
interface AllInOnePrinter {
void print();
void scan();
void fax();
}
// 拆分后:符合ISP
interface Printer { void print(); }
interface Scanner { void scan(); }
interface FaxMachine { void fax(); }
class BasicPrinter implements Printer { /* 仅实现打印 */ }
class AdvancedPrinter implements Printer, Scanner { /* 实现打印和扫描 */ }
四、优缺点分析
优点 |
缺点 |
减少类间耦合,提高扩展性 |
接口数量可能过多,增加管理成本 |
避免客户端实现无用方法 |
需平衡拆分粒度,过度拆分导致复杂度上升 |
提升代码可读性和维护性 |
可能引入重复接口定义(需结合其他设计模式优化) |
五、注意事项
- 适度拆分:接口粒度需结合业务场景,避免“为拆而拆”。
- 高内聚设计:每个接口应具备独立功能,且方法之间高度相关。
- 结合其他原则:如通过依赖注入(DI)或适配器模式灵活组合接口。
六、经典案例
- 美女筛选系统
原接口IPrettyGirl
包含外貌、身材、气质方法。拆分为IGoodBodyGirl
(外形)和IGreatTemperamentGirl
(气质),不同模块按需依赖。 - 数据库操作接口
将通用的增删改查拆分为CRUD
接口和特定功能接口(如分页查询),避免实现类冗余。
通过合理应用接口隔离原则,可以有效优化系统设计,减少“接口污染”,使代码更灵活、健壮。更多案例可参考。