文章目录
- 经典开闭原则实例解析?
- 什么是开闭原则?
- 开闭原则的核心思想
- 案例一:计算器程序
- 不满足开闭原则的情况
- 满足开闭原则的改进方案
- 案例二:图形面积计算
- 不满足开闭原则的情况
- 满足开闭原则的改进方案
- 总结
- 如果你还有其他问题或者需要进一步讨论的地方,请随时告诉我!
- 📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!
经典开闭原则实例解析?
各位码农朋友们,闫工又来给大家讲知识点啦!今天我们要聊一个超级重要的设计原则——开闭原则。这个原则听起来有点抽象,但其实它在我们日常编码中随处可见,甚至可以说是一个“隐形”的指导思想。作为一个有追求的码农,掌握开闭原则不仅能让你写出更优雅的代码,还能让你在面试中秒杀对手!所以,今天闫工就带大家一起来深入理解这个经典的设计原则。
什么是开闭原则?
开闭原则(Open/Closed Principle,简称 OCP)是软件工程中的一个核心设计原则,由Bertrand Meyer在1988年提出。它的定义是:
软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。
换句话说,就是说我们写代码的时候,要让系统能够方便地进行功能扩展,但是尽量避免因为新增功能而修改已有的代码。这样做的好处是什么呢?当然是提高系统的维护性和可扩展性啦!
举个栗子:假设你写了一个计算器程序,支持加减乘除四种运算。后来产品经理突然说还要支持幂运算,这时候你就得想想怎么在不修改原有代码的情况下实现这个新功能。这就是开闭原则的典型应用场景。
开闭原则的核心思想
开闭原则的核心思想是“封闭现有行为,开放未来扩展”。要做到这一点,我们需要遵循以下几个要点:
- 抽象层:通过抽象类或接口来定义系统的行为,而不是具体实现。
- 依赖注入:让组件之间通过接口而非具体实现进行交互。
- 模块化设计:将系统分解成独立的功能模块,每个模块只负责一个职责。
接下来,闫工就带大家通过几个实际案例来理解开闭原则的精髓。
案例一:计算器程序
不满足开闭原则的情况
假设我们有一个简单的计算器程序,支持加减乘除四种运算:
publicclassCalculator{publicintadd(inta,intb){returna+b;}publicintsubtract(inta,intb){returna-b;}publicintmultiply(inta,intb){returna*b;}publicintdivide(inta,intb){if(b==0){thrownewIllegalArgumentException("除数不能为零");}returna/b;}}这个实现看起来很简单,但如果产品经理突然要求支持幂运算怎么办?我们只能在Calculator类中新增一个方法:
publicintpower(intbase,intexponent){return(int)Math.pow(base,exponent);}这样确实满足了需求,但问题在于,每次新增功能都需要修改现有的类。这违背了开闭原则中的“对修改关闭”的要求。
满足开闭原则的改进方案
为了遵守开闭原则,我们需要重新设计这个计算器程序。具体来说,可以使用策略模式来实现。
首先,定义一个运算接口:
publicinterfaceOperation{intcompute(inta,intb);}然后,为每种运算提供具体的实现类:
publicclassAddOperationimplementsOperation{@Overridepublicintcompute(inta,intb){returna+b;}}publicclassSubtractOperationimplementsOperation{@Overridepublicintcompute(inta,intb){returna-b;}}publicclassMultiplyOperationimplementsOperation{@Overridepublicintcompute(inta,intb){returna*b;}}publicclassDivideOperationimplementsOperation{@Overridepublicintcompute(inta,intb){if(b==0){thrownewIllegalArgumentException("除数不能为零");}returna/b;}}接下来,重新设计Calculator类:
publicclassCalculator{privateOperationoperation;publicvoidsetOperation(Operationoperation){this.operation=operation;}publicintcompute(inta,intb){if(operation==null){thrownewIllegalStateException("运算策略未设置");}returnoperation.compute(a,b);}}这样,当我们需要新增幂运算功能时,只需要实现一个新的PowerOperation类:
publicclassPowerOperationimplementsOperation{@Overridepublicintcompute(inta,intb){return(int)Math.pow(a,b);}}然后,在使用计算器时,只需将策略切换到新的PowerOperation即可:
Calculatorcalculator=newCalculator();calculator.setOperation(newAddOperation());System.out.println(calculator.compute(5,3));// 输出: 8calculator.setOperation(newPowerOperation());System.out.println(calculator.compute(2,3));// 输出: 8通过这种设计,我们实现了“对扩展开放,对修改关闭”的目标。每次新增运算类型时,只需要添加新的实现类,而不需要修改原有的代码。
案例二:图形面积计算
再来看一个常见的例子——图形面积计算。假设我们需要支持多种图形的面积计算,比如圆形、矩形、三角形等。
不满足开闭原则的情况
最简单的实现方式可能是这样的:
publicclassAreaCalculator{publicdoublecalculateArea(Stringtype,double...args){switch(type){case"circle":returnMath.PI*args[0]*args[0];case"rectangle":returnargs[0]*args[1];case"triangle":return0.5*args[0]*args[1];default:thrownewIllegalArgumentException("未知图形类型");}}}这个实现虽然简单,但问题在于如果需要新增一种图形(比如五边形),就需要修改calculateArea方法的代码。这显然违反了开闭原则。
满足开闭原则的改进方案
同样的,我们可以使用策略模式来解决这个问题。首先定义一个计算面积的接口:
publicinterfaceAreaStrategy{doublecalculate(double...args);}然后为每种图形提供具体的实现类:
圆形:
publicclassCircleAreaimplementsAreaStrategy{@Overridepublicdoublecalculate(double...args){returnMath.PI*args[0]*args[0];}}矩形:
publicclassRectangleAreaimplementsAreaStrategy{@Overridepublicdoublecalculate(double...args){returnargs[0]*args[1];}}三角形:
publicclassTriangleAreaimplementsAreaStrategy{@Overridepublicdoublecalculate(double...args){return0.5*args[0]*args[1];}}接下来,重新设计AreaCalculator类:
publicclassAreaCalculator{privateAreaStrategystrategy;publicvoidsetStrategy(AreaStrategystrategy){this.strategy=strategy;}publicdoublecalculate(double...args){if(strategy==null){thrownewIllegalStateException("面积计算策略未设置");}returnstrategy.calculate(args);}}这样,当我们需要新增五边形的面积计算时,只需要实现一个新的PentagonArea类:
publicclassPentagonAreaimplementsAreaStrategy{@Overridepublicdoublecalculate(double...args){// 假设参数是边长和半径return1.72048*Math.pow(args[0],2);// 这是一个简化的计算公式}}然后在使用时,只需切换策略即可:
AreaCalculatorcalculator=newAreaCalculator();calculator.setStrategy(newCircleArea());System.out.println(calculator.calculate(5));// 计算半径为5的圆面积calculator.setStrategy(newPentagonArea());System.out.println(calculator.calculate(2,3));// 计算五边形面积通过这种设计,每次新增图形类型时,只需要添加新的策略类,而不需要修改原有的AreaCalculator类。
总结
以上两个案例展示了如何通过策略模式来实现“对扩展开放,对修改关闭”的原则。具体来说:
- 定义接口或抽象类:为需要变化的行为定义一个统一的接口。
- 具体实现类:将不同的行为实现为具体的类,这些类都实现了相同的接口。
- 上下文类:负责维护和切换具体的实现类,并通过这些实现类来处理请求。
这种设计使得系统更加灵活、易于扩展。每次新增功能时,只需添加新的实现类,而不需要修改原有的代码,从而降低了维护成本,提高了系统的可扩展性。
当然,策略模式也有一些缺点:
- 增加了类的数量:每种具体的行为都需要一个独立的类。
- 需要管理多个类:虽然可以通过工厂或其他机制来管理这些类,但仍然需要额外的工作来确保它们被正确地创建和使用。
不过,在大多数情况下,这种权衡是值得的,因为它能够带来更好的代码结构和更高的扩展性。
如果你还有其他问题或者需要进一步讨论的地方,请随时告诉我!
📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!
成体系的面试题,无论你是大佬还是小白,都需要一套JAVA体系的面试题,我已经上岸了!你也想上岸吗?
闫工精心准备了程序准备面试?想系统提升技术实力?闫工精心整理了1000+ 套涵盖前端、后端、算法、数据库、操作系统、网络、设计模式等方向的面试真题 + 详细解析,并附赠高频考点总结、简历模板、面经合集等实用资料!
✅ 覆盖大厂高频题型
✅ 按知识点分类,查漏补缺超方便
✅ 持续更新,助你拿下心仪 Offer!
📥免费领取👉 点击这里获取资料
已帮助数千位开发者成功上岸,下一个就是你!✨