1. 概述

在本篇文章中,我们将深入探讨面向对象设计中的 开闭原则(Open/Closed Principle, OCP),它是 SOLID 设计原则 的核心之一。

我们会从定义出发,结合实际代码示例,讲解如何在 Java 项目中正确应用 OCP。目标是让系统在面对需求变更时,无需修改已有代码即可扩展功能——这才是高质量设计的体现。

2. 开闭原则详解

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

这句话看似简单,但背后隐藏着架构设计的关键思想:
当业务需求变化时,我们应当通过新增代码来实现新功能,而不是去改动已经写好并通过测试的旧代码。否则,每一次修改都可能引入新的 Bug,破坏原有逻辑。

下面我们通过一个“计算器”案例,直观感受 OCP 的实践过程。

2.1. 违反 OCP 的实现方式

假设我们要开发一个简单的计算器,支持加法和减法。初始设计如下:

定义一个顶层标记接口:

public interface CalculatorOperation {}

实现加法操作类:

public class Addition implements CalculatorOperation {
    private double left;
    private double right;
    private double result = 0.0;

    public Addition(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters
}

再实现减法操作类:

public class Subtraction implements CalculatorOperation {
    private double left;
    private double right;
    private double result = 0.0;

    public Subtraction(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters
}

核心的计算器类如下:

public class Calculator {

    public void calculate(CalculatorOperation operation) {
        if (operation == null) {
            throw new InvalidParameterException("Can not perform operation");
        }

        if (operation instanceof Addition) {
            Addition addition = (Addition) operation;
            addition.setResult(addition.getLeft() + addition.getRight());
        } else if (operation instanceof Subtraction) {
            Subtraction subtraction = (Subtraction) operation;
            subtraction.setResult(subtraction.getLeft() - subtraction.getRight());
        }
    }
}

⚠️ 问题来了:

  • 如果现在需要增加“乘法”或“除法”,你得去修改 Calculator.calculate() 方法,加入新的 else if 分支。
  • 每新增一个操作,就要动一次核心类,违背了“对修改关闭”这一条
  • 更严重的是,这种 instanceof + 强制类型转换 的写法,属于典型的“坏味道”,后期维护成本极高。

✅ 结论:这个设计 不符合 OCP

2.2. 符合 OCP 的重构方案

要让 Calculator 类不再随着新操作而修改,关键在于把具体运算逻辑下放到各个操作类内部,并通过统一接口进行调用。

我们重新定义 CalculatorOperation 接口,让它具备执行能力:

public interface CalculatorOperation {
    void perform();
}

加法类自行完成计算逻辑:

public class Addition implements CalculatorOperation {
    private double left;
    private double right;
    private double result;

    public Addition(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters

    @Override
    public void perform() {
        result = left + right;
    }
}

减法类同理:

public class Subtraction implements CalculatorOperation {
    private double left;
    private double right;
    private double result;

    public Subtraction(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters

    @Override
    public void perform() {
        result = left - right;
    }
}

现在来了新需求:支持除法。我们只需要新增一个类即可:

public class Division implements CalculatorOperation {
    private double left;
    private double right;
    private double result;

    public Division(double left, double right) {
        this.left = left;
        this.right = right;
    }

    // getters and setters

    @Override
    public void perform() {
        if (right != 0) {
            result = left / right;
        } else {
            throw new IllegalArgumentException("Cannot divide by zero");
        }
    }
}

最关键的 Calculator 类现在变得极其稳定:

public class Calculator {

    public void calculate(CalculatorOperation operation) {
        if (operation == null) {
            throw new InvalidParameterException("Cannot perform operation");
        }
        operation.perform();
    }
}

亮点分析:

  • 新增功能 = 新增类,无需修改任何已有代码
  • Calculator 类完全解耦了具体运算逻辑,只依赖抽象接口
  • 扩展性强,未来加“幂运算”、“取模”等都只需实现新类
  • 完美践行了“对扩展开放,对修改关闭”

💡 这就是 OCP 的精髓:用多态代替条件判断,用抽象隔离变化。

3. 总结

本文通过一个简单的计算器案例,展示了开闭原则的实际应用场景:

对比项 修改前 修改后
是否符合 OCP
新增功能成本 高(改核心类) 低(新增类)
可维护性 差(易出错) 好(职责清晰)
设计思想 过程式思维 面向对象抽象

🔧 踩坑提醒: 很多开发者一开始都会写出类似 instanceof 判断的代码,看似能跑通,但一旦项目变大就难以维护。记住一句话:当你写 if-elseswitch 来区分类型时,就要警惕是否违反了 OCP。

开闭原则不是银弹,但它是指引我们写出可扩展、易维护代码的重要灯塔。

所有示例代码已托管至 GitHub:https://github.com/java-tutorial-solid-ocp


原始标题:Open/Closed Principle in Java