OOP原则
OOP原则
单一职责原则 (Single Responsibility Principle,SRP)
- 自己的理解:审视实现类的合理性,简化类的功能
- 定义:一个类中应该是一组相关性很高的函数、数据的封装(这个封装可能包括数据和行为)
- 核心价值:防止“上帝类”,提高代码可读性、可维护性,降低修改风险;减少对单个类进行测试时需要mock的依赖
- 重构tips:
- 方法名包含"And"(如
validateAndSave()) - 类名后缀是"Manager"/“Processor”(警惕!)
- 经常修改不同功能区域代码
- 方法名包含"And"(如
开闭原则 (Open-Closed Principle, OCP)
- 自己的理解:审视代码中抽象的合理性,合理的抽象能通过不同实现类将不同细节封装起来,达到灵活拓展效果,更体现多态
- 定义:代码设计时,对象实体应该对拓展开发,对修改封闭
- 通俗解释: 系统新增功能时,应通过添加新代码实现(如继承、实现接口),而非修改已有代码(尤其是线上稳定运行的代码)
- 本质:多态的应用,通过抽象化隔离变化点,外部仅依赖接口或者抽象类
- 核心价值:增强系统扩展性,新功能通过新代码拓展减少测试成本,不用修改旧代码以减少线上BUG
- 重构tips:
- 核心口诀:“封装变化,面向扩展而设计”,模块之间通过仅有抽象或接口依赖
- OCP 是设计模式的目标,策略/观察/适配器模式均是实现OCP的工具
里氏替换原则 (Liskov Substitution Principle, LSP)
- 自己的理解:审视继承或实现的合理性,子类的行为必须大于等于父类的定义
- 定义:子类型必须能替换其基类型,且不影响程序的正确性。
- 通俗解释: 任何使用父类的地方,都能无缝切换成子类**,程序行为不变、不报错、不崩溃。**
- 本质:行为契约一致性,子类必须遵守父类的行为约定,比如子类具有相同行为的父类方法
- 核心价值:增强多态安全性,降低维护成本(天然符合开闭原则)
- 反例:
Arrays.asList()返回的ArrayList是不可变视图,但继承了可变List接口,违反LSP! - 重构tips:
- 调用方是否需要知道具体子类类型?
- 重写方法是否改变了核心行为?
接口隔离原则 (Interface Segregation Principle, ISP)
- 自己的理解:审视接口设计的合理性,主要针对大接口问题,大接口有变动或者有其他实现时容易扩大影响范围
- 定义:客户端不应被迫依赖其不使用的接口方法
- 通俗解释:不要设计臃肿的"上帝接口",讲究接口精简,应该按功能拆分成多个小接口,让实现类只需关心真正需要的方法。
- 本质:高内聚与低耦合,通过接口拆分,减少模块间不必要的耦合
- 功能正交性:每个接口承载单一职责功能
- 最小知识原则**:调用方只需知道与其业务相关的方法**
- 规避接口污染:防止因特定客户需求污染通用接口
- 核心价值:
- 降低耦合度:接口变更仅影响直接相关的实现类
- 提升代码清晰度:小接口自描述性强
- 增强复用性:细粒度接口更易复用
- 重构tips:
- 接口宁小勿大,功能专一而精
- 违反信号
- 实现类出现
UnsupportedOperationException - 调用方引用接口但只使用部分方法
- 接口方法被不同团队不同频次修改
- 实现类出现
依赖倒置原则 (Dependency Inversion Principle, DIP)
自己的理解:审视不同细节(类)依赖关系的合理性。
- 将易变的实现细节隐藏在抽象接口之后。比如service、dao分层设计时,不同层应该通过抽象隔开使两层都依赖这个抽象,而不是service依赖dao细节
定义:高层模块不应依赖低层模块,二者都应依赖抽象 ;**抽象不应依赖细节,细节应依赖抽象
通俗解释:写代码时,业务逻辑(高层)别直接调用数据库/工具类(低层),而是通过接口交互。接口定义契约,实现类负责干活。
本质:通过抽象层隔离变化,实现以下转变:
- 依赖方向倒置 传统:高层 ➜ 依赖 ➜ 低层(如
OrderService直接调MySQLClient) 倒置后:高层 ➜ 依赖 ➜ 抽象 ←─ 实现 ←─ 低层(OrderService调DataSource接口,由MySQLDataSource实现) - 稳定抽象层 抽象接口(如 JDBC、Servlet 规范)是相对稳定的,而具体实现(如 MySQL 驱动、Tomcat)可随意替换
- 依赖方向倒置 传统:高层 ➜ 依赖 ➜ 低层(如
核心价值:
降低耦合:修改数据库类型(MySQL → Oracle)只需换驱动实现,业务层无感知
提升扩展性:新增支付方式(如数字货币支付)实现
便于测试:业务逻辑测试可用 Mock 对象
并行开发:如:业务层逻辑调整、数据持久化层可以分给两个同时开发
重构tips:
- 依赖抽象而非实现,注入而非 new
- 违反信号
- 代码中出现
new 具体类()(除 DTO 等纯数据结构) - 单元测试需启动数据库/消息中间件(这些底层细节应该直接mock)
- 替换技术栈需修改业务代码
- 代码中出现
- DIP 不是银弹:在性能敏感模块(如高频交易)或有强制标准的代码(如银行加密协议)可适度妥协
米迪特原则(Law of Demeter,LoD)
- 自己的理解:审视对象交互的间接性,代码通过链式多级访问对象时,应通过在第一级对象中封装一个方法,将二三级对象访问进行封装。这样如果修改了二三级对象则只需修改第一级对象的方法。这是减少因为修改而扩大影响的原则
- 定义:一个对象应该对其他对象有最少的了解
- 通俗解释:一个对象应尽可能少地了解其他对象,仅与直接关联的对象(朋友)交互,避免深入陌生对象的内部
- 只和直接朋友说话,不和陌生人打交道
- ✅ 朋友:自身(
this)、方法参数、成员对象、自身创建的对象。 - ❌ 陌生人:非直接关联的对象(如
a.getB().getC()中的C)。
- 本质:通过信息隐藏减少依赖,确保模块间通信宽度和深度最小化
- 核心价值:
- 降低耦合:修改一个类时,影响范围仅限于其“朋友”,避免连锁反应
- 增强健壮性:避免深层调用链(如
order.customer.address.city)导致的NullPointerException