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

软件设计原则详解:开闭原则、里氏替换原则、迪米特法则

软件设计三大核心原则(开闭+里氏替换+依赖倒置)全网最细讲解,附Java正反例|面试必背

在日常开发中,你一定遇到过这些痛点:

  • 加个小功能,改出一堆Bug
  • 继承乱用,逻辑越跑越偏
  • 换个数据库/组件,要改几十处代码

这些问题,本质都是没遵守软件设计原则。今天就把面试+工作最常用的三大核心设计原则讲透:开闭原则、里氏替换原则、依赖倒置原则。全文配Java代码正反例,看完就能用在项目里。


前言

设计原则不是玄学,是前人总结的代码健壮性、可扩展、可维护的底层规律。
今天讲的三大原则地位:

  • 开闭原则(OCP):设计原则的核心
  • 里氏替换原则(LSP):继承的黄金标准
  • 依赖倒置原则(DIP):解耦的终极手段

一、开闭原则 OCP

1. 核心思想

对扩展开放,对修改关闭。

  • 扩展:新增功能、新增类、新增实现
  • 关闭:不修改已测试、已上线的稳定代码

一句话:能加就不加改,能扩就不改旧

2. 为什么要遵守?

  • 不改老代码 = 不引入新Bug
  • 系统更稳定、可维护性更强
  • 符合“高内聚低耦合”

生活例子:电脑USB接口。插U盘、鼠标、键盘不用拆电脑,这就是开闭原则。

3. 反例(违反OCP)

// 绘制工具类:用if-else判断类型,违反开闭publicclassShapeDrawer{publicvoiddrawShape(StringshapeType){if(shapeType.equals("Circle")){System.out.println("绘制圆形");}elseif(shapeType.equals("Square")){System.out.println("绘制正方形");}}}

问题:

  • 加三角形必须加else if
  • 改老代码,风险极高
  • 完全违反开闭原则

4. 正例(遵守OCP)

Step1:定义抽象接口

publicinterfaceShape{voiddraw();}

Step2:具体图形实现

publicclassCircleimplementsShape{@Overridepublicvoiddraw(){System.out.println("绘制圆形");}}publicclassSquareimplementsShape{@Overridepublicvoiddraw(){System.out.println("绘制正方形");}}

Step3:稳定的绘制工具

publicclassShapeDrawer{// 依赖抽象,不依赖具体publicvoiddrawShape(Shapes){s.draw();}}

扩展三角形(完全不用改旧代码)

publicclassTriangleimplementsShape{@Overridepublicvoiddraw(){System.out.println("绘制三角形");}}

5. 开闭原则总结

  • 核心:扩展不改旧
  • 关键:面向抽象/接口编程
  • 目的:系统稳定、易扩展、低风险

二、里氏替换原则 LSP

1. 核心思想

子类可以完全替换父类,程序行为不变。

父类能用的地方,子类换上去照样跑,逻辑不崩、结果不错。
这是继承是否合理的唯一标准。

2. 核心理解

  • 继承不是为了复用代码
  • 继承是为了行为统一
  • 子类必须是父类的真正子类型

3. 经典反例(正方形≠长方形)

// 父类:长方形classRectangle{intwidth;intheight;voidsetWidth(intw){width=w;}voidsetHeight(inth){height=h;}}// 子类:正方形(错误继承)classSquareextendsRectangle{voidsetWidth(intw){width=height=w;}voidsetHeight(inth){width=height=h;}}

测试逻辑会崩:

voidresize(Rectangler){r.setWidth(20);// 预期高度不变}

问题:

  • 子类破坏父类行为约定
  • 无法替换父类
  • 违反里氏替换

4. 正例(遵守LSP)

Step1:抽象父类

publicabstractclassShape{publicabstractintgetArea();}

Step2:各自实现

publicclassRectangleextendsShape{privateintw,h;publicRectangle(intw,inth){this.w=w;this.h=h;}@OverridepublicintgetArea(){returnw*h;}}publicclassSquareextendsShape{privateintside;publicSquare(intside){this.side=side;}@OverridepublicintgetArea(){returnside*side;}}

Step3:任意替换

Shapes1=newRectangle(10,20);Shapes2=newSquare(10);

行为完全一致,安全替换。

5. 里氏替换总结

  • 核心:子类能替父类,行为不跑偏
  • 关键:继承看行为,不看代码复用
  • 目的:保证多态安全、系统稳定

三、依赖倒置原则 DIP

1. 核心思想

高层不依赖低层,二者都依赖抽象。
抽象不依赖细节,细节依赖抽象。

大白话:

  • 别直接new具体类
  • 依赖接口/抽象类
  • 实现可以随便换,高层不动

2. 为什么重要?

  • 解耦!解耦!解耦!
  • 换组件不用改高层
  • 方便测试、方便扩展
  • 是Spring/IOC的核心思想

3. 反例(高层依赖具体实现)

// 低层:MySQL实现publicclassMySQLDao{publicvoidqueryUser(){System.out.println("MySQL查询用户");}}// 高层:直接new死,强耦合publicclassUserService{privateMySQLDaomySQLDao=newMySQLDao();publicvoidgetUserInfo(){mySQLDao.queryUser();}}

问题:

  • 换Oracle必须改UserService
  • 紧耦合、难扩展、难测试

4. 正例(依赖抽象)

Step1:抽象接口

publicinterfaceUserDao{voidqueryUser();}

Step2:具体实现

publicclassMySQLDaoimplementsUserDao{@OverridepublicvoidqueryUser(){System.out.println("MySQL查询");}}publicclassOracleDaoimplementsUserDao{@OverridepublicvoidqueryUser(){System.out.println("Oracle查询");}}

Step3:高层依赖抽象

publicclassUserService{privateUserDaouserDao;// 注入:构造/Setter都行publicUserService(UserDaodao){this.userDao=dao;}publicvoidgetUserInfo(){userDao.queryUser();}}

使用:

publicclassTest{publicstaticvoidmain(String[]args){// 随意切换,高层代码完全不动UserServiceservice=newUserService(newMySQLDao());service.getUserInfo();service=newUserService(newOracleDao());service.getUserInfo();}}

5. 依赖倒置总结

  • 核心:依赖抽象,不依赖具体
  • 关键:面向接口编程、依赖注入
  • 目的:极致解耦、高灵活、易扩展

四、三大原则终极总结

原则英文一句话口诀核心解决
开闭原则OCP能扩不改旧扩展风险、稳定性
里氏替换LSP子类能替父类继承安全、多态正确
依赖倒置DIP依赖抽象不绑死解耦、扩展、替换

一句话串起来:
用里氏替换保证继承正确,用依赖倒置实现解耦,最终达到开闭原则——扩展不改、系统稳定。


五、课堂小测(面试常考)

  1. 新增支付方式,直接修改支付工具类 → 违反开闭原则
  2. 子类替换父类后逻辑异常 → 违反里氏替换原则
  3. Service直接new Dao,换库要改代码 → 违反依赖倒置原则

六、总结

这三大原则是设计模式的基础,也是高级工程师必备素养

  • 简单工厂 → 工厂方法,就是为了遵守开闭
  • Spring IOC/AOP,核心就是依赖倒置
  • 合理继承,必须遵守里氏替换

建议收藏,每次写代码前对照一遍,代码质量直接上一个档次。

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

相关文章:

  • ArcGIS空间聚类实战:如何用Grouping Analysis工具快速划分城市功能区(附避坑指南)
  • 2026年评价高的水性工业漆/河北水性工业漆厂家选择推荐 - 行业平台推荐
  • IndexTTS 2.0创意玩法:用AI语音合成制作有声小说,一人搞定全部角色配音
  • 2026年3月知名的石墨接地实力厂家推荐,放热焊接/低电阻接地模块/石墨接地扁带/风力发电接地施工,石墨接地实力厂家推荐 - 品牌推荐师
  • PHP+JS+CSS打造动态星盘计算器
  • 用MATLAB复现近场2D-MUSIC算法:从信号模型到三维谱峰图(附完整代码)
  • 2026年质量好的水性消防管专用防锈漆/河北水性氟碳漆/水性导电工业漆/设备专用水性工业漆厂家选择推荐 - 品牌宣传支持者
  • Rust 宏系统的结构与扩展方法
  • Mipmap实战解析:从纹理闪烁到视觉平滑的渲染优化之路
  • 2026年评价高的模压软木成形/软木/软木杯垫横向对比厂家推荐 - 行业平台推荐
  • P1165 日志分析题解
  • A股站稳4000点:是反弹起点,还是牛市序幕?
  • 小白5090+cuda12.8复现vision Mamba记录
  • AIAgent架构中的对抗攻击防御体系(2024最新NIST合规框架实测版)
  • 【2026唯一权威指南】:基于217家头部企业实测数据,重构AIAgent可观测性、可审计性、可回滚性三角铁律
  • 2026年口碑好的PVC回收/废料PVC回收用户口碑推荐厂家 - 品牌宣传支持者
  • UniApp里用web-view预览PDF?小心这些性能坑和体验优化点
  • Windows 安装 DeerFlow 2.0
  • CasRel模型镜像免配置亮点:预置中文分词器+标点标准化模块
  • AIAgent安全合规红线预警:SITS2026强制要求的6项LLM交互审计日志规范(含审计模板下载)
  • 小白程序员必备:轻松入门大模型Agent,从概念到实战全解析
  • 从数据点到平滑曲线:拉格朗日插值法的原理与实战
  • 华大MCU实战:HC32F460串口IAP升级中的中断向量表重定向与Flash配置
  • 五大页面置换算法实战对比:从理论到实现的性能优化指南
  • 收藏!小白程序员轻松入门大模型,手把手教你做自己的Agent
  • 租户上下文污染、模型缓存穿透、向量库跨租户泄漏……AIAgent架构中5大隐性隔离漏洞(附可审计的OpenTelemetry追踪模板)
  • 一刻相册批量下载工具|免V不限速·原图无损导出·一键傻瓜操作
  • 关于我的第三次web作业
  • 量子密钥分发(QKD)实战:从BB84协议到Python代码实现
  • 三行代码背后的宇宙:当美军封锁霍尔木兹海峡,你的系统能扛住吗?