# PHP与Java面向对象开发:设计模式六大原则之开闭原则详解

做后端开发的朋友,不管你用PHP还是Java,肯定都听过设计模式,也或多或少听过设计模式六大原则,开闭原则基本都是放在第一个讲的。但很多新手朋友看了一堆概念解释,还是搞不懂这原则到底有啥用,写代码的时候该怎么用。
我刚学这块的时候也一样,对着概念抄了好几遍笔记,真到改自己代码的时候,还是忍不住往旧类里堆新逻辑,改完就出一堆bug,越改越乱。今天就用大家都能听懂的话,把开闭原则说清楚,再结合实际代码场景给大家举例子,看完就能明白怎么用在自己的开发里。
先说说开闭原则最核心的定义,其实就一句话:对扩展开放,对修改关闭。
这句话听起来挺玄乎,翻译成人话其实就是,当你的项目需求变了,你尽量不要去改原来已经写好、跑稳定了的核心代码,而是通过加新代码的方式来实现新功能。
举个最生活化的例子,你家里原来装的是普通插座,现在你买了个新的usb插头的台灯,你会直接把墙里的电线重新挖出来改插座结构吗?肯定不会啊,你买个带usb口的插排插上去就能用了。这里原来的墙里插座就是已经写好不能随便改的旧代码,新插排就是我们加的扩展代码,这不就是对扩展开放对修改关闭嘛。
那为什么非要这么做呢?很多人说我改两句旧代码怎么了,原来的代码不就是给人改的吗?
这事儿得说清楚,小项目小功能,改两句确实没事,甚至改了更快。但项目越做越大,参与的人越来越多,原来的旧代码已经跑了很久,很多其他功能都依赖它。你随便改里面的逻辑,哪怕改一个判断条件,都有可能把原来没问题的功能改出bug。而且改的越多,代码里的依赖就越乱,到最后整个类改得几千行,谁都不敢动,这不就是大家常说的屎山代码吗?
我们做面向对象开发,不管PHP还是Java,核心就是把代码拆成一个个职责清晰的类,降低各个模块之间的耦合。开闭原则就是帮你做到这一点的最基础的规则。
我们拿实际的开发场景举例子,就拿电商系统里最常见的用户打折优惠来说吧。刚做项目的时候,运营说普通会员打9折,高级会员打8折,你很容易就写出了这么一段代码,我们伪代码写出来大家都能懂。
你先写了一个DiscountCalculator类,里面有个算价格的方法,传用户等级和原价进去,用if判断等级,然后返回折扣后的价格,逻辑很简单,跑起来也没问题。
过了半个月,运营说要加一个活动,新用户打7折,还有超级会员打5折。你想都没想,直接回到原来的DiscountCalculator类里,加了两个if判断,改完上线,也能用。
再过一个月,运营又说要搞节日专属折扣,不同节日折扣不一样,还要加上满减后的叠加折扣。你这时候再回去加if,就发现不对了,这个方法都快一百行了,加一个新折扣就得改一次原来的代码,每次改完都得把原来的折扣逻辑重新测一遍,怕哪里改出问题,万一不小心改了原来的判断条件,老会员折扣错了,那就是大事故。
这个就是典型的违反开闭原则的写法,所有的折扣逻辑都堆在一个类里,需求一变就改原来的代码,越改越乱。
那按照开闭原则该怎么改呢?其实很简单,我们把不同的折扣逻辑抽出来,做一个公共的折扣接口,接口里定好一个getDiscount方法,不管什么折扣,都要实现这个方法。
然后原来的普通会员折扣、高级会员折扣,都各自做一个实现这个接口的类,每个类只管自己的折扣逻辑。原来的DiscountCalculator类就不用存那么多if了,只需要接收传过来的折扣对象,调用它的getDiscount方法算价格就行。
这时候运营再加新的折扣,我们根本不用改原来的任何代码,只需要新建一个类实现折扣接口,写好新的折扣逻辑,直接传进去用就好了。原来的代码还是原来那样,跑了那么久都没问题,根本不会被影响,也不会不小心改出bug。
你看,这不就是我们说的,对扩展开放,加新功能只要写新类,对修改关闭,原来稳定的代码不用改。
很多朋友看完这个例子会说,那不就是多写了好几个类吗?看起来代码变多了,有必要吗?
其实这个问题很好回答,当你的项目只需要一两个折扣,确实没必要,直接写if反而更快。但项目发展起来,需求不断加,这种写法就能帮你把每个功能的逻辑拆解开,不会所有东西都堆在一起。就算以后要改某个折扣的逻辑,你只改对应的那个类就好了,其他类都不动,风险小很多。
这里还要说一个新手常犯的误区,开闭原则不是说绝对不能改旧代码,一点都动不得。如果原来的旧代码本身就有bug,那肯定要改啊。或者项目小,新加的逻辑很简单,改一下比加新类方便,那改了也没什么。
开闭原则是一个设计思路,不是写死的规矩,它是帮你提前应对未来的需求变化,减少代码改坏的风险。你写代码的时候多想想,这个地方以后会不会变,如果会变,那我就把变的部分抽出来,留好扩展的口子,不要把所有逻辑都写死在一个地方。
还有朋友会问,我们用PHP和Java,在落地开闭原则的时候有什么不一样吗?其实核心思路完全一样,都是面向接口编程,抽离变化的部分。PHP虽然是弱类型,但也一样可以写接口,一样可以做多态实现,思路是通的,只是语法不一样而已。
我自己开发这么多年,最大的感受就是,开闭原则不是那种为了凑设计模式*的东西,它是真的能解决实际问题的。我刚参加工作那会,接了一个老项目,所有的业务逻辑都写在一个大控制器里,加个新功能就得在几百行的方法里加if,每次上线都提心吊胆,改完这个bug那个又出问题。后来慢慢按照开闭原则重构,把每个变的业务逻辑抽成单独的类,加新功能只要加新类,后来上线出错的概率直接降了一大半。
对刚开始学设计模式的朋友来说,不用一开始就逼着自己所有代码都严格遵守,先把这个思路记在脑子里,写代码的时候遇到需求变更,多想想,我现在改旧代码会不会影响别的地方,能不能用扩展的方式做?做多了慢慢就养成习惯了。
说白了,开闭原则的本质,就是把变化和稳定分开,稳定的部分不动,变化的部分扩展,这样你的代码才能越写越稳,不会做着做着就变成没人敢动的屎山。不管你是做PHP开发还是Java开发,面向对象开发里这个思路是通用的,把这个原则摸透了,再学其他设计模式,你一下就能看懂为啥那些模式要这么设计,很多原来想不通的地方也会豁然开朗。

开闭原则,设计模式六大原则,PHP面向对象,Java面向对象,面向对象开发,设计模式原则,对扩展开放对修改关闭,开闭原则应用,PHP开发,Java开发

[Q]:什么是开闭原则?
[A]:开闭原则的核心就是"对扩展开放,对修改关闭",意思是项目需求变更时,尽量不修改已经稳定运行的旧代码,通过添加新代码实现新需求,避免修改旧代码引发潜在bug。
[Q]:为什么要遵守开闭原则?
[A]:项目规模变大后,很多功能都依赖已经稳定的旧代码,随意修改旧代码很容易引发新bug,还会让代码越来越臃肿,变成难以维护的屎山代码,开闭原则能降低代码耦合,减少修改风险。
[Q]:PHP可以用开闭原则吗?
[A]:可以,开闭原则是面向对象开发的通用设计原则,和开发语言无关,不管是PHP还是Java,核心思路都是抽离变化、面向接口编程,只需要适配对应语法即可。
[Q]:开闭原则就是绝对不能改旧代码吗?
[A]:不是,开闭原则是设计思路不是死规则,如果旧代码本身存在bug,或者小项目简单需求改旧代码更高效,是可以修改的,它的核心是提前应对变化,不是禁止一切修改。
[Q]:违反开闭原则的常见写法是什么?
[A]:最典型的就是把所有变化的逻辑都堆在同一个类里,比如新增折扣优惠时,直接在原来的折扣计算类里不停加if判断,每次新增需求都要修改原有代码,越改越乱。
[Q]:怎么在实际开发中应用开闭原则?
[A]:一般是先抽离公共接口,把变化的不同逻辑拆分成独立的类来实现接口,原有核心类只需要调用接口方法即可,新增需求只需要新建实现类,不需要修改原有代码。
[Q]:开闭原则会让代码变多,有必要吗?
[A]:小项目简单需求确实可以不用复杂拆分,需求会不断变更的中大型项目里,这种拆分是很有必要的,能降低后续维护和新增功能的风险,避免代码臃肿混乱,长期来看收益远大于多写几个类的成本。
[Q]:开闭原则和设计模式是什么关系?
[A]:开闭原则是设计模式六大原则的核心基础,大部分设计模式都是为了满足开闭原则才诞生的,掌握开闭原则能帮你更好理解其他设计模式的设计思路。
share