设计模式中的原则
本文是【GoF设计模式】系列的前置篇前言本篇可以当做设计模式学习前的入门也可以当成设计模式学习后的复习。先看一段典型的面条代码// 一个臃肿的 UserService校验、持久化、通知、日志全堆在一起publicclassUserService{publicvoidregister(Useruser){if(user.getName()null||user.getName().isEmpty()){thrownewIllegalArgumentException(用户名不能为空);}ConnectionconnDriverManager.getConnection(jdbc:mysql://localhost/db,root,root);// 保存、发邮件、记日志全在这里换数据库要改源码也无法单独测试}}这段代码职责过多、强依赖具体实现、难以替换和测试。随着代码增长这类结构会越来越难维护。设计原则正是从这些痛点中提炼出的经验总结能让代码组织得更灵活、可维护、可测试。面向对象设计中有几个经典原则前五个常被合称为SOLID缩写原则核心要义S单一职责原则SRP一个类只承担一个职责O开闭原则OCP对扩展开放对修改关闭L里氏代换原则LSP子类必须能替换父类I接口隔离原则ISP不依赖不需要的接口D依赖倒转原则DIP依赖抽象不依赖细节此外还有迪米特法则LoD和合成/聚合复用原则CARP。下面逐一介绍。单一职责原则SRP单一职责原则Single Responsibility Principle就一个类而言应该仅有一个引起它变化的原因。如果一个类承担的职责过多就等于把这些职责耦合在一起一个职责的变化可能削弱或抑制它完成其他职责的能力导致脆弱的设计。软件设计的重要内容之一就是发现职责并把它们相互分离。判断一个类是否职责过多可以看是否能想到多个动机去改变它或用一句话描述它的职责若出现和或等连接词往往说明违反了该原则。❌违反 SRP 的设计// 一个类承担了数据管理、权限验证、消息通知三个职责publicclassUserService{publicvoidsaveUser(Useruser){/* 保存用户 */}publicbooleancheckPermission(Useruser,Stringresource){/* 检查权限 */}publicvoidsendEmail(Useruser,Stringmessage){/* 发送邮件 */}}✅遵循 SRP 的设计// 按职责拆分每个类只负责一件事publicclassUserService{publicvoidsaveUser(Useruser){/* 保存用户 */}}publicclassPermissionService{publicbooleancheckPermission(Useruser,Stringresource){/* 检查权限 */}}publicclassNotificationService{publicvoidsendEmail(Useruser,Stringmessage){/* 发送邮件 */}}优点缺点降低类的复杂度每个类只负责一项职责增加类的数量系统复杂度上升提高代码可读性与可维护性过度拆分会增加类间依赖修改一个职责不影响其他职责需要合理判断职责边界开闭原则OCP开闭原则Open-Closed Principle软件实体类、模块、函数等应该可以扩展但是不可修改。即对扩展开放、对修改封闭。无论模块多么封闭都会存在无法封闭的变化设计人员需要猜测最可能发生的变化再构造抽象来隔离它们。开闭原则是面向对象设计的核心遵循它能带来可维护、可扩展、可复用、灵活性好等好处。但拒绝不成熟的抽象和抽象本身一样重要不要对每个部分都刻意抽象。实现开闭原则的常见方式抽象与多态用接口或抽象类定义抽象层客户端依赖抽象而非具体实现参数化把变化的部分抽象为参数策略对象、回调配置化可变部分提取到配置文件运行时读取配置决定实现元数据驱动用注解驱动框架行为新增逻辑靠加注解而非改框架❌违反 OCP 的设计// 每次新增支付方式都要修改这里的 if/elsepublicclassPaymentProcessor{publicvoidprocessPayment(Stringtype,BigDecimalamount){if(alipay.equals(type)){/* 支付宝 */}elseif(wechat.equals(type)){/* 微信 */}}}✅遵循 OCP 的设计// 通过抽象扩展新增支付方式只需新增类不改老代码publicinterfacePaymentStrategy{voidpay(BigDecimalamount);}publicclassAlipayStrategyimplementsPaymentStrategy{publicvoidpay(BigDecimalamount){/* 支付宝 */}}publicclassPaymentProcessor{privatePaymentStrategystrategy;publicPaymentProcessor(PaymentStrategystrategy){this.strategystrategy;}publicvoidprocessPayment(BigDecimalamount){strategy.pay(amount);}}优点缺点提高系统稳定性减少回归测试需要预判变化增加设计难度提高代码复用性与可维护性增加抽象层提高复杂度新功能通过扩展实现降低修改风险过度抽象会让代码难懂依赖倒转原则DIP依赖倒转原则Dependency Inversion Principle高层不应依赖低层两者都依赖抽象抽象不应依赖细节细节应依赖抽象依赖倒转可以说是面向对象设计的标志如果编写时考虑的都是针对抽象编程而非细节编程即所有依赖关系都终止于抽象类或接口就是面向对象的设计反之就是过程化设计。传统过程化设计中高层依赖低层依赖倒转把这个关系倒转过来让两者都依赖抽象从而解耦。实现方式是高层模块定义接口、低层模块实现接口再用依赖注入把低层模块注入高层模块做到面向接口编程而非面向实现编程。❌违反 DIP 的设计// 高层直接 new 低层具体实现换数据库要改源码publicclassUserService{privateMySQLUserDaouserDaonewMySQLUserDao();publicUsergetUser(intid){returnuserDao.findById(id);}}✅遵循 DIP 的设计// 高层依赖抽象低层实现抽象通过构造器注入publicinterfaceUserDao{UserfindById(intid);}publicclassMySQLUserDaoimplementsUserDao{publicUserfindById(intid){/* MySQL 查询 */}}publicclassUserService{privateUserDaouserDao;publicUserService(UserDaouserDao){this.userDaouserDao;}publicUsergetUser(intid){returnuserDao.findById(id);}}优点缺点降低类间耦合提高系统稳定性增加抽象层提高复杂度提高可扩展性便于替换具体实现需要较好的抽象能力便于并行开发与单元测试轻松 Mock 依赖依赖注入框架增加学习成本里氏代换原则LSP里氏代换原则Liskov Substitution Principle子类型必须能替换父类型。只有当子类可以替换父类、软件单位的功能不受影响时父类才能真正被复用子类也能在父类基础上增加新行为。里氏代换是继承复用的基石只有子类能完全替换父类时继承才是合理的设计。经典反例是正方形继承矩形正方形重写setWidth/setHeight强制宽高相等导致在期望矩形的地方替换为正方形时面积计算出错。正确做法是让矩形和正方形共同实现一个Shape接口而非用继承强行关联。Java 集合框架中任何使用List接口的地方都能无缝替换为ArrayList、LinkedList就是该原则的体现。❌违反 LSP 的设计// 正方形继承矩形重写方法强制宽高相等替换后面积计算出错publicclassRectangle{protectedintwidth,height;publicvoidsetWidth(intw){widthw;}publicvoidsetHeight(inth){heighth;}publicintgetArea(){returnwidth*height;}}publicclassSquareextendsRectangle{publicvoidsetWidth(intw){widthw;heightw;}publicvoidsetHeight(inth){widthh;heighth;}}✅遵循 LSP 的设计// 矩形和正方形共同实现 Shape 接口不再用继承强行关联publicinterfaceShape{intgetArea();}publicclassRectangleimplementsShape{privateintwidth,height;publicRectangle(intw,inth){widthw;heighth;}publicintgetArea(){returnwidth*height;}}publicclassSquareimplementsShape{privateintside;publicSquare(intside){this.sideside;}publicintgetArea(){returnside*side;}}优点缺点保证继承复用的正确性限制继承的灵活性提高可维护性子类不破坏父类行为需仔细设计继承关系便于统一测试父类行为可能导致类层次变深迪米特法则LoD迪米特法则Law of Demeter又叫最少知识原则如果两个类不必直接通信就不应直接相互作用需要调用时通过第三者转发。它强调每个类都应尽量降低成员的访问权限一个对象应该对其他对象有尽可能少的了解只与直接朋友通信不跟陌生人说话。直接朋友对象自身、成员对象、方法参数、方法内创建的对象陌生人通过方法返回值间接获得的对象等a.getB().doSomething()这种链式调用属于和陌生人说话违反该法则。Java 三层架构中 Controller 只调用 Service、Service 只调用 Repository就是迪米特法则的典型应用。❌违反 LoD 的设计// 房客直接与房东、银行等多个“陌生人”交互publicclassTenant{publicvoidrentRoom(){LandlordlandlordnewLandlord();BankbanknewBank();landlord.negotiate();// 直接调用房东bank.transfer();// 直接调用银行}}✅遵循 LoD 的设计// 房客只与中介直接朋友交互由中介转发调用publicclassTenant{privateAgentagent;publicvoidrentRoom(){agent.rentRoom();}}publicclassAgent{privateLandlordlandlord;privateBankbank;publicvoidrentRoom(){landlord.negotiate();bank.transfer();}}优点缺点降低类间耦合提高模块独立性可能产生大量中介类提高可读性与可维护性过度使用会使结构复杂修改一个类影响范围更小可能增加方法调用层数接口隔离原则ISP接口隔离原则Interface Segregation Principle客户端不应该依赖它不需要的接口一个类对另一个类的依赖应建立在最小接口上。要建立单一的接口不要建立庞大臃肿的接口接口中的方法应尽量少只包含客户端需要的方法。接口过大时实现类被迫实现用不到的方法产生冗余代码。这里的隔离不是物理隔离而是通过拆分接口让客户端只依赖需要的方法与不需要的方法隔离。与单一职责原则的区别对比维度单一职责原则SRP接口隔离原则ISP关注点类的职责业务功能接口的依赖范围判断依据引起变化的原因客户端需要的方法拆分对象类接口两者经常配合SRP 保证类职责单一ISP 保证接口粒度合适。典型例子是 Java 集合的Iterable/Collection/List/RandomAccess接口层次客户端可按需依赖最小接口。❌违反 ISP 的设计// 臃肿接口机器人被迫实现用不到的 eat、sleeppublicinterfaceWorker{voidwork();voideat();voidsleep();}publicclassRobotWorkerimplementsWorker{publicvoidwork(){/* 工作 */}publicvoideat(){}// 空实现机器人不吃饭publicvoidsleep(){}// 空实现机器人不睡觉}✅遵循 ISP 的设计// 拆分接口机器人只实现需要的接口publicinterfaceWorkable{voidwork();}publicclassRobotWorkerimplementsWorkable{publicvoidwork(){/* 工作 */}}优点缺点降低接口耦合客户端只依赖需要的方法接口数量增多复杂度上升提高内聚性接口职责单一拆分过细可能导致类爆炸减少冗余实现扩展不影响已有接口需合理判断接口粒度合成/聚合复用原则CARP合成/聚合复用原则Composite/Aggregate Reuse Principle优先用合成/聚合少用类继承。继承关系在编译时就固定下来无法在运行时改变子类与父类紧密耦合父类的任何变化都会波及子类限制了灵活性和复用性。合成/聚合则保持每个类的封装性让类继承层次保持较小规模。合成与聚合都是关联的特殊种类聚合是弱的拥有关系部分可独立存在如学生与班级合成是强的拥有关系部分与整体同生命周期如人与心脏。❌违反 CARP 的设计// 用继承复用引擎汽车 IS-A 引擎逻辑不对且引擎变化会波及汽车publicclassCarextendsEngine{// 引擎的任何改动都会影响汽车}✅遵循 CARP 的设计// 用合成复用引擎汽车 HAS-A 引擎运行时可切换publicinterfaceEngine{voidstart();}publicclassCar{privateEngineengine;publicCar(Engineengine){this.engineengine;}publicvoidstart(){engine.start();}}对比维度继承合成/聚合耦合度高编译时绑定低运行时可替换灵活性低高复用性受限于父类实现可组合多个对象封装性破坏封装保持封装适用场景IS-A 关系狗是动物HAS-A 关系汽车有引擎优点缺点降低类间耦合度可能产生大量小类提高灵活性与可扩展性对象组合可能让结构复杂支持运行时动态组合、保持封装初期设计成本较高原则与设计模式的对应设计原则是设计模式的灵魂每个 GoF 模式背后都对应着一条或多条原则。理解这层映射能在遇到问题时快速定位该用哪个模式原则典型对应的设计模式体现方式单一职责SRP门面模式、桥接模式按职责拆分类与接口开闭OCP策略、装饰、观察者、模板方法通过新增类扩展功能不改已有代码里氏代换LSP策略、模板方法、状态子类安全替换父类多态有意义接口隔离ISP门面模式、适配器模式为客户端提供最小接口依赖倒转DIP工厂方法、抽象工厂、依赖注入高层依赖抽象低层实现抽象迪米特法则中介者、外观模式通过中介/门面减少直接交互合成/聚合复用CARP桥接、装饰、策略、代理、组合用组合代替继承原则之间的关系这些原则并非孤立存在而是相互支撑、形成完整的面向对象设计思想体系开闭原则核心目标 ↑ ┌──────────────┼──────────────┐ │ │ │ 单一职责原则 依赖倒转原则 里氏代换原则 类的拆分 抽象层解耦 继承的正确使用 │ │ │ └──────────────┼──────────────┘ ↓ 接口隔离原则接口拆分 ↓ 迪米特法则降低耦合 ↓ 合成/聚合复用原则复用方式开闭原则是核心目标让系统易于扩展、难以修改单一职责通过职责拆分为开闭原则创造条件依赖倒转通过抽象层解耦实现开闭原则里氏代换保证继承的正确性使多态有意义接口隔离通过拆分臃肿接口让依赖更精确迪米特法则通过减少通信降低耦合合成/聚合复用通过组合提高灵活性和复用性。这些原则有时也会相互掣肘需要权衡接口隔离拆接口 vs 合成复用导致类爆炸只有当不同客户端确实只用到接口的一部分时才拆分依赖倒转抽象层 vs 简单性抽象的前提是变化真实存在或可预见变体只有一种时直接依赖具体实现反而更清晰迪米特减少通信 vs 中介类泛滥中介只在需要降低跨层耦合时引入不为内部协作加壳原则不是强制规定不要为了遵循原则而过度设计。先让代码简单正确地跑起来等到变化真正出现时再重构引入抽象——重复三次的代码才考虑抽象。记忆口诀拆分靠 SRP一个类只做一件事扩展靠 OCP加功能不改老代码换实现靠 DIP都依赖抽象运行时替换继承看 LSP子类能替父类多态才安全接口靠 ISP用不到的方法别塞过来通信靠 LoD只跟直接朋友说话复用靠 CARP能用组合就别用继承一句话总纲先简单正确再按需抽象拆得清职责换得动实现扩得起新功能。

相关新闻

缓存友好的数据结构设计原则与实现案例

缓存友好的数据结构设计原则与实现案例

缓存友好的数据结构设计原则 局部性原理的应用 时间局部性(频繁访问的数据集中存储)与空间局部性(相邻数据预加载)是核心原则。结构设计应确保热点数据在物理内存中连续分布,减少缓存行(Cache Line&#xf…

2026/7/29 1:24:07阅读更多 →
Java Socket 多人聊天室(私聊+群聊)技术笔记(V4版本)

Java Socket 多人聊天室(私聊+群聊)技术笔记(V4版本)

一、项目整体架构 本项目是基于 BIO 阻塞式 Socket 实现的桌面多人聊天室,支持用户登录、群聊、一对一私聊功能,搭配Swing实现可视化客户端界面。整体分为服务端、客户端、用户实体、UI界面四大核心模块,采用 服务端中转消息 的通信模式&…

2026/7/29 1:24:07阅读更多 →
Anthropic 无人机实验:端到端能力“突然变强”,往往只是最后一个子任务补齐了

Anthropic 无人机实验:端到端能力“突然变强”,往往只是最后一个子任务补齐了

你线上的 Agent 是不是也这样: Demo 时飞得挺漂亮,上线后却撞墙、丢目标、偶发才成功同事说“模型升级了,端到端通了”,你却不知道该信峰值还是均值产品想砍人工审核,你又拿不出“什么时候可以少盯一眼”的标准 这不…

2026/7/29 1:24:07阅读更多 →
OpenSmith:本地LLM Pipeline追踪与调试实战指南

OpenSmith:本地LLM Pipeline追踪与调试实战指南

在本地开发和调试 LLM 应用时,你是否遇到过这样的困境:想要追踪每个组件的输入输出、查看中间结果、分析性能瓶颈,却发现现有的工具要么需要接入云端服务,要么配置复杂、难以集成?特别是在涉及敏感数据或需要离线工作的…

2026/7/29 2:30:19阅读更多 →
libsvm python模型秒变API?MLServer这招绝了,不用Flask也能跑

libsvm python模型秒变API?MLServer这招绝了,不用Flask也能跑

你有没有碰到过这种情形, 就是当你训练好了一个新的模型之后, 有时候你不想费神去编写那个Flask Code(也就是web框架), 也不想把模型进行容器化并且在某个环境里运行它, 只是想要借助API马上就能使用这个模型?要是你存在这样的需求, 那必然是会希望去了…

2026/7/29 2:30:19阅读更多 →
基于热释电红外传感器与Arduino的智能安防报警系统DIY全攻略

基于热释电红外传感器与Arduino的智能安防报警系统DIY全攻略

1. 项目概述:从“被动防御”到“主动预警”的跨越提起家庭或小型场所的安全防护,很多人第一时间想到的是摄像头。但摄像头更多是“事后追溯”的工具,它记录下发生了什么,却无法在入侵发生的瞬间发出即时警报。而红外报警装置&…

2026/7/29 2:30:19阅读更多 →
基于Arduino与状态机的智能红绿灯系统设计与实现

基于Arduino与状态机的智能红绿灯系统设计与实现

1. 项目概述:从“拍脑袋”到“动手做”的智能路口构想每次在路口等红绿灯,看着空无一人的斑马线和倒计时几十秒的红灯,心里总会冒出个念头:这时间是不是有点浪费?尤其是深夜或者车流稀少的时段,让行人干等那…

2026/7/29 2:30:19阅读更多 →
国内直连免费AI工具GPT-5.6与GPT Image2实测指南

国内直连免费AI工具GPT-5.6与GPT Image2实测指南

这次我们来看一个近期关注度很高的技术方案:如何在国内环境下免费、无需特殊网络配置就能使用 GPT-5.6 Sol 和 GPT Image2 这两个模型。如果你一直在找能在手机和电脑上直接访问的 AI 工具,并且希望避开复杂的部署流程或账号限制,这篇文章会给…

2026/7/29 2:30:19阅读更多 →
从零搭建AI智能体:MCP协议、工具调用与工作流实战指南

从零搭建AI智能体:MCP协议、工具调用与工作流实战指南

从零搭建 AI 智能体(Agent)已经成为开发者进入大模型应用领域的关键技能。无论是企业内部流程自动化、数据分析助手,还是复杂的多步骤任务规划,掌握智能体的核心组件和搭建流程都能让你在实际项目中快速落地 AI 能力。本文将以工程…

2026/7/29 2:28:19阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“! 在构建复杂的 Agent 系统时,我们经常会遇到这样的场景:Agent 正在执行一个多步骤的任务,比如“下单购买商品”,但执行到一半时,我们…

2026/7/29 0:01:46阅读更多 →
自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…

2026/7/29 0:01:46阅读更多 →
【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:01:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →