当前位置: 首页 > news >正文

从CRM图表重构,吃透「开闭原则」

题目里的原始需求很简单:Sunny的CRM系统要支持显示不同类型的业务图表,现在已经做了饼状图PieChart和柱状图BarChart,要求前端展示类ChartDisplay来根据类型调用不同图表的显示方法,原始设计类图就是题目给的这样:

我们先把原始设计翻译成大家能看懂的代码,感受一下问题出在哪:

Java

// 饼状图类 class PieChart { public void display() { System.out.println("显示饼状图"); } } // 柱状图类 class BarChart { public void display() { System.out.println("显示柱状图"); } } // 图表显示类(原始版本) class ChartDisplay { public void display(String type) { // 根据传入的类型判断显示哪种图表 if ("pie".equals(type)) { new PieChart().display(); } else if ("bar".equals(type)) { new BarChart().display(); } } }

这个代码写出来,能用吗?当然能用。但问题在哪?我们想想业务迭代:下个月产品经理说,我们要给CRM加个折线图LineChart,还要加个雷达图RadarChart,你要怎么改?

你只能跑到ChartDisplaydisplay方法里,再加两个if else分支——每加一种新图表,就要修改原有显示类的代码​。

这就犯了软件开发的大忌:

  1. 修改原有代码很容易不小心改出bug,原来好好的饼状图显示,你加新分支的时候碰了原来的逻辑,直接把旧功能搞崩了
  2. 代码越堆越长,最后display方法变成几百行的"面条代码",谁看谁头疼
  3. 新增功能必须改核心代码,完全不符合我们说的开闭原则。

开闭原则到底要解决什么问题?

我们先回忆一下开闭原则的定义:

软件实体(类、模块、方法等)应该对扩展开放,对修改关闭——也就是添加新功能的时候,尽量不要修改原有的、已经测试好的代码,而是通过扩展新代码来实现需求。

为什么要这么做?核心就是把变化的风险隔离起来​:已经上线测试过的稳定代码,改得越少,出bug的概率越低。对于我们这个图表的例子来说,变化点是什么?就是"不断新增的图表类型"——所以我们要把图表变化这个部分抽出来,让加新图不用改原来的代码。

重构:用抽象+多态实现开闭原则

要实现开闭,核心思路就是把变化的部分抽象成抽象层,具体实现交给子类扩展​。我们梳理一下重构步骤:

步骤1:抽出抽象图表父层/接口

不管是什么类型的图表,都要实现display()方法,所以我们先定义一个抽象的AbstractChart,规定所有图表的统一行为:

Java

// 抽象图表抽象类:定义所有图表的公共行为 public abstract class AbstractChart { public abstract void display(); }

如果你更喜欢用接口,也可以写成接口形式,这里用抽象类不影响核心逻辑:

Java

public interface Chart { void display(); }

步骤2:具体图表继承抽象层,各自实现

原来的PieChartBarChart都改成抽象层的子类,各自实现自己的显示逻辑就好:

Java

// 饼状图:扩展抽象图表 public class PieChart extends AbstractChart { @Override public void display() { System.out.println("显示饼状图"); } } // 柱状图:扩展抽象图表 public class BarChart extends AbstractChart { @Override public void display() { System.out.println("显示柱状图"); } }

步骤3:重构显示类,去除条件判断

原来的ChartDisplay不需要再根据type写一堆if else了,现在它只需要面向抽象的AbstractChart编程,不管来什么图表,调用统一的display()就可以:

Java

public class ChartDisplay { // 直接接收抽象图表,不需要判断类型 public void display(AbstractChart chart) { chart.display(); } }

重构完的类图结构就变成了这样:

┌─────────────────────────────────┐ │ AbstractChart(抽象层) │ │ + display() : void │ └─────────────┬───────────────────┘ │ ┌──────┴──────┐ ▼ ▼ ┌───────────┐ ┌───────────┐ │ PieChart │ │ BarChart │ │ +display()│ │ +display()│ └───────────┘ └───────────┘ ┌────────────────────┐ │ ChartDisplay │ │ +display(AbstractChart) │ └────────────────────┘

加新图表有多爽?看看开闭原则的优势

现在我们再回头看刚才的需求:要加折线图,怎么做?

  1. 新增一个LineChart类,继承AbstractChart,实现自己的display()——搞定,不用改任何原来的代码
  2. 客户端直接把新的折线图传给ChartDisplay就可以显示。

代码写出来就是这样:

Java

// 新增折线图:只需要扩展新类,不需要改原来的ChartDisplay、其他图表的代码 public class LineChart extends AbstractChart { @Override public void display() { System.out.println("显示折线图"); } }

客户端调用:

Java

public class Client { public static void main(String[] args) { ChartDisplay display = new ChartDisplay(); // 显示饼状图 display.display(new PieChart()); // 显示柱状图 display.display(new BarChart()); // 新增折线图:不需要改任何原有逻辑,直接用 display.display(new LineChart()); } }

完美!原来的ChartDisplay是稳定的,原来的PieChartBarChart也是稳定的,所有稳定代码我们一行都没改,只加了新的扩展类,完全符合开闭原则。

如果以后还要加雷达图、散点图、热力图,都是一模一样的流程,只加新类,不改旧代码,从根源上避免了改旧代码出bug的风险。

一些更灵活的扩展:如果需要适配第三方图表库?

实际开发里我们经常会遇到一种情况:我们用到的第三方图表库,它的类已经写好了,没法改成我们继承AbstractChart的结构怎么办?这时候我们可以用适配器模式配合开闭原则,一样不用改原有代码:

比如我们拿到一个第三方的柱状图类,接口和我们不兼容:

Java

// 第三方的柱状图类,我们改不了它的源码 public class ThirdPartyBarChart { // 它的方法叫show(),不是我们要求的display() public void show() { System.out.println("第三方柱状图渲染"); } }

我们不需要改第三方的代码,也不需要改我们的ChartDisplay代码,只需要加一个适配器类,扩展我们的抽象图表就好:

Java

// 适配器:扩展我们的AbstractChart,适配第三方类 public class ThirdPartyBarChartAdapter extends AbstractChart { private ThirdPartyBarChart thirdChart = new ThirdPartyBarChart(); @Override public void display() { // 转调第三方的show方法 thirdChart.show(); } }

还是那个逻辑:加新类,不改旧代码,完美兼容。

总结:开闭原则的核心不是"永远不修改"

很多人会误解开闭原则:开闭原则就是说永远不能改原有代码吗?其实不是,开闭原则是一种设计思想:​我们要把会变化的部分提前抽出来抽象化,让后续新增需求的时候,尽量通过扩展而不是修改来实现,以此来保证系统的稳定性​。

回到我们这个CRM的例子,重构前后的对比其实很明显:

原始设计重构后(符合开闭)
新增图表需要修改ChartDisplay代码新增图表只需要加新类,不用改原有代码
条件分支越来越多,代码可读性差代码职责清晰,符合单一职责
修改旧代码容易引入bug稳定代码不修改,风险隔离

其实开闭原则是所有设计模式的核心基调,很多设计模式(策略、装饰、适配器、工厂等等)本质上都是为了符合开闭原则。吃透这个小案例,再看其他设计模式,你一下子就能get到设计背后的思路了~

http://www.jsqmd.com/news/1045943/

相关文章:

  • 如何快速恢复加密压缩包密码:ArchivePasswordTestTool完整使用教程
  • 动态图特征空间跟踪技术G-REST算法解析
  • 实时处理器用户级中断硬件优化与实现
  • HS2-HF_Patch技术深度解析:构建Honey Select 2终极增强生态的架构实践
  • 2026年中广东钣金设备外观设计公司推荐:洞察行业趋势与优选服务商 - 品牌鉴赏官2026
  • 【图像加密】混合混沌移位变换和于修正 Henon映射的图像加密算法密码分析【含Matlab源码 15646期】
  • Beyond Compare 5密钥生成器:3种方法完整指南
  • 2026年湖北专业聚合配送调度系统更新解析:数字化时代的商家降本增效新引擎 - 品牌鉴赏官2026
  • 2026百色漏水检测维修精选优质服务商TOP5推荐!卫生间漏水/厨房漏水/屋顶天花板漏水/阳台漏水/地下室漏水防水补漏检测维修-正规防水补漏公司优选口碑榜测评推荐 - 即刻修防水
  • 2026贵阳2026正规漏水检测维修公司精选口碑榜TOP5权威推荐-精准定位检测漏水点-专业防水补漏堵漏维修、卫生间/厨房/屋顶/天沟/地下室/阳台防水漏水检测维修 - 安佳防水
  • SuperCom:面向工业级串口调试的智能化解决方案
  • 3分钟掌握宝可梦随机化:让经典游戏焕发新生
  • 那个“超2000万人在用“的工具,有一个细节没人告诉你
  • 3步实现零代码办公自动化:免费RPA工具taskt终极指南
  • 告别Flash时代终结的遗憾:CefFlashBrowser让你的经典游戏和应用重获新生
  • 2026年6月,新中式家具口碑好的实力工厂推荐速览,实木套系家具/榫卯结构新中式家具,新中式家具源头厂家找哪家 - 品牌推荐师
  • PowerPMAC实战指南:从零到精通的EtherCAT配置与调试
  • MinecraftForge模组开发终极指南:从零开始打造你的第一个模组
  • GanttProject 5步精通:免费开源项目管理工具的完整指南
  • 3个惊人技巧:在VS Code中直接编辑Word/Excel文档,告别频繁切换软件
  • 商铺户外外摆仿真植物花箱:江浙沪高耐晒仿真花箱与仿真植物材质落地指南 - 三棵树园艺
  • 告别家务焦虑!北京全城派单的“真旺居保洁”,凭什么成为无数家庭与企业的首选? - 本地品牌推荐
  • 2026盐城本地人必选防水补漏检测维修公司靠谱服务商TOP5推荐:房屋渗漏水检测维修/卫生间/厨房/天花板/阳台/外墙渗漏水检测补漏维修-暗管漏水检测专业仪器精准定位漏水点 - 即刻修防水
  • 多保真度代理模型在翼型优化中的应用与实现
  • 2026百色本地人必选防水补漏检测维修公司靠谱服务商TOP5推荐:房屋渗漏水检测维修/卫生间/厨房/天花板/阳台/外墙渗漏水检测补漏维修-暗管漏水检测专业仪器精准定位漏水点 - 即刻修防水
  • Source Han Serif思源宋体:专业级开源中文字体配置与实战指南
  • 青岛芯片级无人机维修服务商深度解析:2026年新消息聚焦专业、信誉与高效 - 品牌鉴赏官2026
  • KFS异构数据同步软件
  • 影刀RPA异常处理进阶:自愈机制、告警通知与故障转移设计
  • 解锁 QWebEngineView 视频播放能力:从编译参数到实战替换