OOP原则

OOP原则

单一职责原则 (Single Responsibility Principle,SRP)

  • 自己的理解:审视实现类的合理性,简化类的功能
  • 定义:一个类中应该是一组相关性很高的函数、数据的封装(这个封装可能包括数据和行为)
  • 核心价值:防止“上帝类”,提高代码可读性、可维护性,降低修改风险减少对单个类进行测试时需要mock的依赖
  • 重构tips:
    • 方法名包含"And"(如validateAndSave()
    • 类名后缀是"Manager"/“Processor”(警惕!)
    • 经常修改不同功能区域代码

开闭原则 (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) 倒置后:高层 ➜ 依赖 ➜ 抽象 ←─ 实现 ←─ 低层(OrderServiceDataSource 接口,由 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