news 2026/8/26 1:19:42

设计模式-基础篇(六大原则)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式-基础篇(六大原则)

按照,单,开,里,依,接,迪的顺序,这是一篇详细介绍了六大设计原则的文章,以通俗,易懂的语言进行说明,并配套有对应的实例,代码尽可能的简单。非常适合新手入门或者老手回顾,复习。
也是我自己的一次对设计模式原则的回顾。

单一职责

所谓的单一职责,简单理解就是每一个类只负责一件事情,但是往往在开发中,我们会为了某些业务进行妥协,破坏了单一职责。比如说,一个商品新增的service,我们有可能在里面为了顺手,写上商品删除,修改等逻辑。最终,这个service就变成了商品处理的职责。
很多人会纠结在商品整体处理的层面上,这么写好像做到了单一职责,但是细化到增删改单个功能,又感觉破坏了单一职责。其实单一职责没有绝对的死板标准,核心判断逻辑是:一个类只有一个修改的理由。
通俗解释:只要这个类的所有功能,都是因为同一个业务需求变更需要修改,那就是符合单一职责的。不用过度钻牛角尖、过度拆分代码,设计原则的本质是为了简化开发,不用太过于条条框框,应该因地制宜、灵活使用。

代码说明

违反单一职责的例子
// 违背单一职责:一个类承担多个商品操作职责publicclassGoodsService{// 新增商品publicvoidaddGoods(){System.out.println("新增商品逻辑");}// 修改商品publicvoidupdateGoods(){System.out.println("修改商品逻辑");}// 删除商品publicvoiddeleteGoods(){System.out.println("删除商品逻辑");}}
符合单一职责的列子
// 只负责商品新增,仅新增需求变更时才会修改publicclassGoodsAddService{publicvoidaddGoods(){System.out.println("新增商品逻辑");}}// 只负责商品修改,仅修改需求变更时才会修改publicclassGoodsUpdateService{publicvoidupdateGoods(){System.out.println("修改商品逻辑");}}// 只负责商品删除,仅删除需求变更时才会修改publicclassGoodsDeleteService{publicvoiddeleteGoods(){System.out.println("删除商品逻辑");}}

补充说明,其实日常使用,在小型项目中,如果公司没有严格上的规范要求,为了简单省事儿,大部分情况下,大家都用第一种。
统一的 GoodsService(商品整体操作)只有「商品业务规则」这一个变更理由,也是符合单一职责的,完全可以因地制宜使用。

开闭原则

对扩展开放,对修改关闭。我的理解是本质上就是担心在修改原有代码的过程中,不小心改坏了原有的正常功能。因此,有新的业务需求,最好不要改动老代码,而是单独扩展新代码。
但是,这个原则在日常开发中,也不绝对。有时候,我们遇到一些很简单的修改,比如单纯替换一个常量、修改一段固定文案,这种简单改动只要细心核对,一般不会有什么大问题,不用刻意死板扩展。
而对于新增的、复杂的业务逻辑,则最好单独写一个方法、单独抽离逻辑进行调用,不要在原有成熟代码里进行大量编写、堆砌新逻辑。

代码说明

违反开发原则示例
// 原有成熟代码:基础商品折扣服务publicclassDiscountService{// 原有默认折扣:普通用户9折publicdoublegetDiscount(doubleprice){returnprice*0.9;}}// 新增需求:需要VIP用户8折折扣// 错误写法:直接修改原有成熟方法,修改原有代码,违背开闭原则publicclassDiscountService{publicdoublegetDiscount(doubleprice,StringuserType){// 原有逻辑if("normal".equals(userType)){returnprice*0.9;}// 新增逻辑:直接塞到老方法中,改动原有代码if("vip".equals(userType)){returnprice*0.8;}returnprice;}}
符合开闭原则的写法
// 原有成熟代码:完全不改动、保留原有逻辑publicclassDiscountService{publicdoublegetDiscount(doubleprice){returnprice*0.9;}}// 新增VIP折扣业务:单独扩展新类,拓展新功能publicclassVipDiscountServiceextendsDiscountService{// 扩展新的VIP折扣逻辑,不修改父类原有代码publicdoublegetVipDiscount(doubleprice){returnprice*0.8;}}

这样做的好处就是,已经上线的代码我们没有改动过,不影响原有功能。我的建议是,小范围更改可以灵活变动,但是复杂业务一定要使用开闭原则。

里氏替换原则

这个名称听起来有点奇怪,但我们不用关注他的名字,只需要牢牢记住,所谓的里氏替换,就是父类可以被使用的地方,子类也可以完全替代父类使用,并且不会超出父类的能力限定范围,程序不会出现异常。
这里有一个核心重点:子类重写方法后,允许个性化实现,但不能违背父类的行为契约。
所谓的契约其实就是父类对外承诺的能力。比如父类鸟类承诺自己可以飞行,这就是对外的契约。子类麻雀、老鹰可以各自实现不一样的飞行效果,这是正常的个性化重写,完全符合规则;但如果子类鸵鸟继承鸟类,却重写方法表示不会飞、直接报错,就违背了父类的契约,彻底破坏了里氏替换原则。
子类可以和父类、其他子类的执行细节不一样,但是父类能做到的事,子类必须也能做到,不能翻车、报错、失效。

代码说明

反面例子
// 父类:鸟类,对外契约:所有鸟都会飞classBird{publicvoidfly(){System.out.println("鸟儿可以正常飞行");}}// 子类:鸵鸟(违背里氏替换)classOstrichextendsBird{// 破坏父类契约:父类会飞,子类不会飞@Overridepublicvoidfly(){// 空实现/抛异常,功能失效System.out.println("鸵鸟不会飞行");}}// 测试方法:接收父类BirdpublicclassBirdTest{publicstaticvoidletFly(Birdbird){bird.fly();}publicstaticvoidmain(String[]args){// 传入父类正常运行letFly(newBird());// 传入子类,逻辑失效,违背里氏替换letFly(newOstrich());}}
正面例子
// 父类:鸟类,契约:所有鸟都会飞classBird{publicvoidfly(){System.out.println("鸟儿可以正常飞行");}}// 子类:麻雀(合规重写,个性化实现)classSparrowextendsBird{@Overridepublicvoidfly(){System.out.println("麻雀在低空自由飞行");}}// 子类:老鹰(合规重写,个性化实现)classEagleextendsBird{@Overridepublicvoidfly(){System.out.println("老鹰在高空展翅飞行");}}// 测试方法:任意子类都可以完美替换父类publicclassBirdTest{publicstaticvoidletFly(Birdbird){bird.fly();}publicstaticvoidmain(String[]args){letFly(newBird());letFly(newSparrow());letFly(newEagle());}}

所以,里氏替换,重点其实就是父类的定义。写好前期的规范,约束,后期大家都进行遵守,达到子类,父类,完美替换的效果。

依赖倒置原则

很多人会混淆继承关系和依赖关系,一个常见误区就是依赖倒置的核心不是父子类之间的依赖,而是项目中所有的高层模块、低层模块,都依赖抽象(接口/抽象类),不依赖具体的实现类。

网上有一句话:子类依赖于父类,父类不依赖于子类。这句话本身就是开发通用准则,和父类是不是抽象类、接口没有关系,不存在对错之分,任何时候都不允许父类依赖子类。

抽象是固定的规范,具体实现是多变的。我们写代码要盯着固定的规范写,不要盯着具体的业务实现写。这样后续改需求、换实现方式的时候,不用改动核心代码,更加灵活。

代码说明

反面例子
// 具体实现类:微信支付(底层具体功能)publicclassWechatPay{publicvoidpay(){System.out.println("微信支付成功");}}// 高层业务模块:直接依赖具体实现类 WechatPaypublicclassPayService{// 死死绑定具体类,无法灵活替换privateWechatPaypay=newWechatPay();publicvoiddoPay(){pay.pay();}}
正面例子
// 抽象规范:支付接口(固定不变的抽象层)publicinterfacePay{voidpay();}// 底层实现1:微信支付(可变的具体实现)publicclassWechatPayimplementsPay{@Overridepublicvoidpay(){System.out.println("微信支付成功");}}// 底层实现2:支付宝支付(后续可无限扩展)publicclassAliPayimplementsPay{@Overridepublicvoidpay(){System.out.println("支付宝支付成功");}}// 高层业务模块:只依赖抽象接口,不依赖具体类publicclassPayService{// 面向抽象编程,灵活替换任意支付实现privatePaypay;publicPayService(Paypay){this.pay=pay;}publicvoiddoPay(){pay.pay();}}// 测试调用publicclassPayTest{publicstaticvoidmain(String[]args){// 随时切换实现,无需改核心代码newPayService(newWechatPay()).doPay();newPayService(newAliPay()).doPay();}}

依赖倒置,高层和底层都进行依赖抽象。一开始就需要定义好抽象约束,并且,要使用记住,实现依赖于抽象,抽象绝不能绑定具体的实现。

接口隔离原则

所谓的接口隔离,其实与单一职责有点像,但是也有很大的不同。单一职责有时候需要我们主观上去区分这个职责是否单一,就比如有些人认为,商品处理类,处理商品增删改查,都包含在里面,属于单一职责。而有些人则认为,增删改查也可以抽离出来做单一职责,这个相对比较主观,仁者见仁智者见智。
而接口隔离和它最大的不同就是,接口隔离是客观的、有明确标准的。核心规则就是:一个类如果实现了某个接口,就不应该存在不需要实现的空方法、无效方法。如果一个接口里有很多无用方法,被迫让实现类空实现,就说明这个接口太臃肿,需要拆分。

代码说明

反面例子
// 臃肿、未拆分的大接口publicinterfaceGoodsOperation{// 商品新增voidaddGoods();// 商品修改voidupdateGoods();// 商品删除voiddeleteGoods();// 商品查询voidqueryGoods();}// 只需要新增功能的实现类,被迫实现所有无用方法publicclassGoodsAddImplimplementsGoodsOperation{@OverridepublicvoidaddGoods(){System.out.println("执行商品新增");}// 无用方法,被迫空实现(严重违背接口隔离)@OverridepublicvoidupdateGoods(){}@OverridepublicvoiddeleteGoods(){}@OverridepublicvoidqueryGoods(){}}
正面例子
// 拆分后:单一功能细粒度接口publicinterfaceGoodsAdd{voidaddGoods();}publicinterfaceGoodsUpdate{voidupdateGoods();}publicinterfaceGoodsDelete{voiddeleteGoods();}publicinterfaceGoodsQuery{voidqueryGoods();}// 实现类按需实现,无冗余、无空方法publicclassGoodsAddImplimplementsGoodsAdd{@OverridepublicvoidaddGoods(){System.out.println("执行商品新增");}}// 需要多个功能可多实现接口,灵活组合publicclassGoodsFullImplimplementsGoodsAdd,GoodsUpdate,GoodsDelete,GoodsQuery{@OverridepublicvoidaddGoods(){System.out.println("执行商品新增");}@OverridepublicvoidupdateGoods(){System.out.println("执行商品修改");}@OverridepublicvoiddeleteGoods(){System.out.println("执行商品删除");}@OverridepublicvoidqueryGoods(){System.out.println("执行商品查询");}}

这样做的好处就是,自己实现自己的,但是,这个例子其实不是很恰当,因为大部分情况下,如此简单的业务逻辑,我们会用上面哪个反面例子来写。

迪米特法则

这个名字也有点奇怪,但是换一个名字,大家就知道了。所谓的迪米特法则,其实就是最少知道原则。学过java的同学知道,在Java的三大特性中,有一个很重要的特性,就是封装。调用封装的方法,我们不需要管封装里面是怎么样处理的,只需要知道,这个方法需要的输入参数是什么,会产生什么样的结果即可。
在这个基础上,去理解迪米特法则就很简单了:调用方不需要了解被调用方的内部逻辑、代码细节,只需要通过公开的方法完成调用,知道最终的执行效果就行。尽量减少类与类之间的关联,知道的越少,代码耦合度越低,越不容易出bug。

代码说明

反面例子
// 学生类publicclassStudent{// 公开属性(暴露内部细节)publicStringname;publicintage;}// 老师类(严重违背迪米特法则)publicclassTeacher{// 直接访问学生的内部属性,过度知道对方细节publicvoidshowStudentInfo(Studentstudent){System.out.println("学生姓名:"+student.name);System.out.println("学生年龄:"+student.age);}}
正面例子
// 学生类:封装内部细节,只暴露公开方法publicclassStudent{// 私有内部属性,禁止外部访问privateStringname;privateintage;publicStudent(Stringname,intage){this.name=name;this.age=age;}// 仅对外提供公开方法,隐藏内部细节publicStringgetStudentInfo(){return"学生姓名:"+name+",学生年龄:"+age;}}// 老师类:最少知道,只调用公开方法publicclassTeacher{publicvoidshowStudentInfo(Studentstudent){// 只拿结果,不问过程,不碰内部细节System.out.println(student.getStudentInfo());}}// 测试调用publicclassTest{publicstaticvoidmain(String[]args){Studentstudent=newStudent("张三",18);newTeacher().showStudentInfo(student);}}

简单来说,就是自己处理自己的,调用方只需要知道调用可以达到什么样的效果即可,而不是帮助处理非自己的属性,变量。

以上,就是我对设计模式六大原则的全部理解。如今 AI 发展迅速,确实能帮我们快速生成代码、完成基础开发,但我始终认为,AI 永远需要人来主导、驱动。

AI 可以实现代码,却不具备业务前瞻性,无法预判未来的需求迭代与代码隐患。实际开发中,业务需求多变是常态,如果完全依赖 AI 重写代码,不仅频繁消耗开发时间,也会造成大量无效资源损耗。

所以,真正高效的编码方式是:人理清业务逻辑、提前预判迭代风险、遵循合理的设计原则,再借助 AI 辅助开发。人机相辅相成,才能让代码更稳健、拓展性更强,在复杂的业务迭代中游刃有余,在编码的道路上稳步前行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 1:19:18

现场活动画面组织控制及抽奖的使用疑难问题汇编

环境前期准备及设置: 1. 双屏前期设置方法[又名:系统扩展桌面设置方法-双屏双显扩展桌面技术前期设置方法-PPT分屏技术设置方法] http://blog.csdn.net/boomcode/article/details/5311322 2. 双屏扩展桌面类软件,展示界面位置不正确,或相反,如何解决? 双屏扩展桌面类软件,…

作者头像 李华
网站建设 2026/8/26 1:19:17

论多源异构数据集成与数据架构设计

一、项目背景、数据源构成与架构分工我曾负责某大型制造集团的企业级数据平台建设项目。该集团旗下拥有多条业务线,涵盖生产制造、供应链管理、市场营销、售后服务和设备运维等领域,数字化转型过程中积累了极为庞杂的数据资产。数据源构成方面&#xff0…

作者头像 李华
网站建设 2026/8/26 1:18:59

2026年烟台做智慧排水监测系统的公司前10名有哪些?

从烟台山脚往东走,几公里内能连续经过陡坡、台地和滨海路,一场急雨里水往哪走、走多快,住在不同坡度的烟台人感受完全不同。南高北低的地势把山洪快速压向市区管网,而黄海的风暴潮又会在汛期顶托排水口,内水外排受阻&a…

作者头像 李华
网站建设 2026/8/25 23:57:18

PyWenCai:用 Python 调用同花顺问财,5 分钟搭好金融数据采集管道

PyWenCai:用 Python 调用同花顺问财,5 分钟搭好金融数据采集管道 【免费下载链接】pywencai 获取同花顺问财数据 项目地址: https://gitcode.com/gh_mirrors/py/pywencai PyWenCai 是一个 Python 金融数据获取工具,把同花顺问财的查询…

作者头像 李华
网站建设 2026/8/25 23:54:47

144、影像算法DSP/NPU Offloading——降噪/超分/语义分割算子在高通/联发科/海思平台的部署选择

144、影像算法DSP/NPU Offloading——降噪/超分/语义分割算子在高通/联发科/海思平台的部署选择 上个月在调试一台搭载骁龙8 Gen2的旗舰机,客户反馈夜景模式在暗光下预览帧率掉到18fps,而且发热明显。抓了systrace一看,罪魁祸首是自研的时域降噪算子——纯CPU跑,每帧耗时3…

作者头像 李华