CBA
面向对象设计原则(面向对象编程(OOP)中五个核心设计原则SOLID)

SOLID 是由 Robert C. Martin(又称 Uncle Bob)提出的面向对象编程(OOP)中五个核心设计原则。这些原则旨在帮助开发者设计出更易维护、可扩展、灵活且健壮的软件系统。SOLID 适用于任何面向对象语言(如 Java、C#、Python、C++ 等),其目标是降低代码耦合度、提高内聚性,并增强系统的可测试性和可重用性。


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

定义:

一个类应该只有一个引起它变化的原因。

换句话说,一个类只负责一项职责。如果一个类承担了多个职责,那么当其中一个需求发生变化时,可能会影响其他不相关的功能。

为什么重要?

  • 减少类之间的耦合。
  • 提高可读性和可维护性。
  • 更容易进行单元测试。
  • 修改一个功能不会影响其他功能。

示例(反面教材):

class User {    private String name;    private String email;    public void saveToDatabase() {  }    public void sendEmail() {  }}

这个 User 类既负责用户数据管理,又负责持久化和通知,违反了 SRP。

改进后:

class User {    private String name;    private String email;    // 只包含用户属性和基本行为}class UserRepository {    public void save(User user) {  }}class EmailService {    public void sendEmail(User user) {  }}

2.开闭原则(Open/Closed Principle, OCP)

定义:

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭

也就是说,当需要增加新功能时,应通过扩展(如继承、组合、接口实现)来实现,而不是直接修改已有代码。

为什么重要?

  • 避免因修改现有代码引入 bug。
  • 提高系统的稳定性与可扩展性。
  • 符合“不要重复自己”(DRY)和“封装变化”的思想。

示例:

假设有一个图形绘制程序:

class Circle {  }class Square {  }class GraphicEditor {    public void drawShape(Object shape) {        if (shape instanceof Circle) {            // 绘制圆形        } else if (shape instanceof Square) {            // 绘制方形        }    }}

每次新增图形都要修改 drawShape 方法,违反 OCP。

改进(使用多态 + 接口):

interface Shape {    void draw();}class Circle implements Shape {    public void draw() {  }}class Square implements Shape {    public void vsraw() {  }}class GraphicEditor {    public void drawShape(Shape shape) {        shape.draw(); // 无需修改    }}

现在添加新图形只需实现 Shape 接口,无需改动 GraphicEditor。


3.里氏替换原则(Liskov Substitution Principle, LSP)

定义:

子类型必须能够替换它们的基类型而不改变程序的正确性。

即:任何父类出现的地方,子类都应该能无缝替代,且不引发异常或逻辑错误

为什么重要?

  • 保证继承体系的合理性。
  • 避免因子类“破坏”父类契约导致运行时错误。
  • 是实现多态的基础保障。

经典反例:正方形-长方形问题

class Rectangle {    protected int width, height;    public void setWidth(int w) { width = w; }    public void setHeight(int h) { height = h; }    public int getArea() { return width * height; }}class Square extends Rectangle {    @Override    public void setWidth(int w) {        width = height = w;    }    @Override    public void setHeight(int h) {        width = height = h;    }}

在以下代码中:

void test(Rectangle r) {    r.setWidth(5);    r.setHeight(4);    assert r.getArea() == 20; // 对 Square 会失败(面积为16)}

Square 不能安全替换 Rectangle,违反 LSP。

解决方案:

  • 避免不合理的继承关系。
  • 使用组合代替继承。
  • 或重新设计类层次结构(例如,让 Square 和 Rectangle 都实现 Shape 接口,而非继承)。

4.接口隔离原则(Interface Segregation Principle, ISP)

定义:

客户端不应该被迫依赖于它们不使用的接口。

即:不要定义“胖接口”(包含太多方法),而应拆分为多个小而专注的接口

为什么重要?

  • 减少不必要的依赖。
  • 提高模块的内聚性。
  • 避免实现类被迫实现空方法(“接口污染”)。

反例:

interface Worker {    void work();    void eat();    void sleep();}class Human implements Worker {    public void work() {  }    public void eat() {  }    public void sleep() {  }}class Robot implements Worker {    public void work() {  }    public void eat() { throw new UnsupportedOperationException(); } // 不合理!    public void sleep() { throw new UnsupportedOperationException(); }}

Robot 被迫实现不需要的方法。

改进:

interface Workable {    void work();}interface Eatable {    void eat();}interface Sleepable {    void sleep();}class Human implements Workable, Eatable, Sleepable {  }class Robot implements Workable {  }

5.依赖倒置原则(Dependency Inversion Principle, DIP)

定义:

高层模块不应该依赖低层模块,二者都应该依赖抽象。

抽象不应该依赖细节,细节应该依赖抽象。

通俗地说:依赖接口或抽象类,而不是具体实现

为什么重要?

  • 解耦高层业务逻辑与底层实现细节。
  • 便于替换实现(如切换数据库、日志系统等)。
  • 是实现控制反转(IoC)和依赖注入(DI)的基础。

反例:

class MySQLDatabase {    public void connect() {  }}class UserService {    private MySQLDatabase db = new MySQLDatabase(); // 直接依赖具体类    public void saveUser() {        db.connect();        // 保存用户    }}

如果要换成 PostgreSQL,必须修改 UserService。

改进(依赖抽象):

interface Database {    void connect();}class MySQLDatabase implements Database {    public void connect() {  }}class PostgresDatabase implements Database {    public void connect() {  }}class UserService {    private Database db;    // 通过构造函数注入依赖(依赖注入)    public UserService(Database db) {        this.db = db;    }    public void saveUser() {        db.connect();        // 保存用户    }}

现在可以轻松切换数据库实现,且 UserService 不再与具体数据库耦合。


总结表格

原则 缩写 核心思想 关键词 单一职责原则 SRP 一个类只做一件事 职责分离 开闭原则 OCP 扩展开放,修改关闭 多态、抽象 里氏替换原则 LSP 子类可替换父类 行为一致性 接口隔离原则 ISP 小接口优于大接口 按需依赖 依赖倒置原则 DIP 依赖抽象,而非实现 控制反转


实践建议

  • 不要教条化:SOLID 是指导原则,不是硬性规则。过度设计反而有害。
  • 结合设计模式:如策略模式(OCP)、工厂模式(DIP)、模板方法(LSP)等常用于实现 SOLID。
  • 配合测试:遵循 SOLID 的代码更容易单元测试(尤其是 DIP 和 SRP)。
  • 重构是关键:初期不必完美遵循,但随着需求演进,应逐步向 SOLID 靠拢。

顶一下()     踩一下()

热门推荐

发表评论
0评