我们先看一下原始设计的问题所在:
ChartDisplay类里有一个display(String type)方法,通过传入type来判断是显示饼状图(PieChart)还是柱状图(BarChart)。- 这种设计存在明显缺陷:每次新增一种图表类型(比如折线图、雷达图),都必须修改
ChartDisplay类的代码,在display方法里增加新的判断分支。 - 这直接违反了开闭原则(Open-Closed Principle, OCP):软件实体应当对扩展开放,对修改关闭。
二、重构思路:用抽象和多态实现开闭原则
开闭原则的核心实现方式,就是抽象化定义接口 / 抽象类,再让不同的具体实现类继承 / 实现它。我们的重构步骤如下:
1. 定义抽象图表接口 / 抽象类
首先创建一个Chart抽象类(或接口),定义所有图表都必须实现的display()方法。
java
运行
// 抽象图表类 public abstract class Chart { public abstract void display(); }2. 让具体图表类继承抽象类
让PieChart、BarChart继承Chart类,实现各自的display()方法:
java
运行
// 饼状图类 public class PieChart extends Chart { @Override public void display() { System.out.println("显示饼状图"); } } // 柱状图类 public class BarChart extends Chart { @Override public void display() { System.out.println("显示柱状图"); } }3. 改造ChartDisplay类
ChartDisplay不再依赖具体的图表类型,而是依赖抽象的Chart类,通过多态调用display()方法:
java
运行
public class ChartDisplay { // 不再通过type判断,而是直接接收Chart对象 public void display(Chart chart) { chart.display(); } }4. 客户端调用示例
java
运行
public class Client { public static void main(String[] args) { ChartDisplay display = new ChartDisplay(); // 显示饼状图 Chart pie = new PieChart(); display.display(pie); // 显示柱状图 Chart bar = new BarChart(); display.display(bar); } }三、扩展场景:新增图表类型时的变化
当我们需要新增 “折线图(LineChart)” 时,只需要做两件事:
- 新增
LineChart类,继承Chart并实现display()方法:
java
运行
public class LineChart extends Chart { @Override public void display() { System.out.println("显示折线图"); } }- 客户端直接使用新的图表类,无需修改
ChartDisplay:
java
运行
// 新增折线图调用 Chart line = new LineChart(); display.display(line);✅ 可以看到:新增功能时,我们没有修改任何已有代码,完全通过扩展实现了需求,完美符合开闭原则。
四、重构前后对比
表格
| 维度 | 原始设计 | 重构后设计 |
|---|---|---|
| 新增图表时 | 必须修改ChartDisplay类的display方法,增加判断分支 | 只需新增一个继承Chart的类,无需修改已有代码 |
| 耦合度 | ChartDisplay与所有具体图表类强耦合 | ChartDisplay仅依赖抽象Chart,与具体实现解耦 |
| 扩展性 | 差,每次扩展都要修改核心类 | 强,扩展时不影响已有代码 |
| 维护成本 | 高,修改可能引入新的 bug | 低,新增功能对已有逻辑无影响 |
五、开闭原则的核心要点
- 抽象是关键:通过抽象类 / 接口定义稳定的契约,具体实现可以自由扩展。
- 对扩展开放:新需求通过新增类来实现,而不是修改旧代码。
- 对修改关闭:已有的、稳定的业务代码不应该被频繁修改,避免引入风险。
- 依赖抽象,而非具体:高层模块(如
ChartDisplay)只依赖抽象,不依赖具体实现类。