恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
设计模式复习指南:七大原则与22种模式Java落地实战
首页
资讯中心
/
设计模式复习指南:七大原则与22种模式Java落地实战
设计模式复习指南:七大原则与22种模式Java落地实战
发布时间:2026/10/15 6:45:42
简介一份面向软件工程领域的设计模式复习文档为具备编程基础、尤其是熟悉面向对象设计的13年研发人员编写帮助理解设计模式概念并指导实际项目选型。压缩包内为单个docx文件大小约3.66MB内容紧凑完整。文档系统梳理设计模式的定义、分类、特点与应用场景详细阐述单一职责、开放-封闭、依赖倒转、里氏代换、迪米特法则、合成/聚合、接口隔离七大设计原则。重点剖析抽象工厂、策略、外观、适配器、组合、单例、中介者七种常用模式并覆盖其余十五种模式每种均以定义、UML类图、基础代码和实例讲解便于对照练习。还对比不同模式的差异总结模式选择关键维度附练习题与模拟试卷。目前已有189人学习适合复习备考或项目设计中做选型参考。1. 设计模式复习22 种模式、七大原则与两套自测卷子怎么用设计模式这个词对工作 1-3 年的 Java 开发者来说是面试要背、重构要抄、看源码被虐的三重存在。手头这份《大话设计模式复习》不是入门教程而是把 22 种模式拆成定义、UML 类图、基础代码、实例四件套的复习提纲前面是七大设计原则后面是易混对比和两套自测卷子。适合谁用对面向对象设计有一定基础、写过业务代码、被 if-else 和继承体系折磨过、准备面试或要重构旧项目的人。它最值钱的地方不在背模式名而在用七大原则反向判断这个场景该不该上模式、上哪个这正是软件工程里模式应用能力的分水岭。2. 七大设计原则从代码味道到选型速查表的三个落点2.1 原则是模式的根七条原则各自的代码信号文档把七大原则放在 22 种模式前面这个顺序本身就是复习路线。我见过不少一上来就背类图的同学结果被问为什么策略模式符合开放-封闭原则直接卡住。原则是根模式是枝叶先看懂根才知道枝叶往哪长。单看每一条原则它们对应的代码信号完全不同。单一职责原则SRP说的是一个类只有一个引起它改变的理由——一个类里既做数据校验、又写日志、还管数据库操作这就是 SRP 被违反的信号。文档拿计算器举例加法和减法各自独立成类而不是揉在一个运算类里。开放-封闭原则OCP是对扩展开放、对修改封闭它要求你提前识别变化点观察者、策略、装饰这三种模式本质上都是在不修改原有类的前提下预留扩展位。依赖倒转原则DIP要求高层模块和低层模块都依赖抽象工厂模式里客户端依赖抽象产品接口而非 new 具体产品类就是它的标准落地。里氏代换原则LSP管的是继承契约子类必须能替换父类且行为一致矩形和正方形那个经典例子子类悄悄改了前置条件替换后面积计算就出问题。迪米特法则LOD说的是两个类不必直接通信就不要直接通信中介者模式是它的极端实现日常代码里更多体现为控制方法调用链的深度。合成/聚合原则CARP提醒你优先用组合而不是继承装饰模式一层层包裹而不是一层层继承就是组合优于继承的样板。接口隔离原则ISP把胖接口拆成瘦接口把动物拆成飞行动物陆地动物避免实现类被迫实现一堆用不到的方法。2.2 原则冲突时的优先级以订单折扣模块为例七条原则不是天生和睦的。DIP 要求依赖抽象但小项目里硬上抽象会多出一堆接口类LOD 要求不直接通信但为了转发硬绕一圈中介反而把简单调用变复杂。我实际排优先级是这样OCP 最高因为你不可能预知所有扩展方向DIP 其次它直接决定代码能不能做单元测试SRP 和 ISP 更像代码味道探测器闻到再动手不用提前铲平。拿商场收银的折扣逻辑举例。早期版本把正常收费、满减、打折全部写在一个 if-else 链里每新增一种营销活动就要改一遍核心类。按 OCP 和 SRP 重构后每种折扣独立成策略类新增活动只加类、不动旧逻辑按 DIP 重构后调用方依赖策略接口而不是具体折扣类测试时可以直接注入 mock 策略。同一个案例把三条原则串起来这就是文档原则指导模式的完整含义。另一个容易被忽略的是 LSP 在重构中的实际影响。很多继承体系初期合理后来某个子类为了省事把父类方法直接重写成完全不同的行为编译能过运行起来在用父类的地方全部翻车。检查手段就一句话把子类对象当父类用跑一遍父类的所有单元测试挂了就说明 LSP 被破坏。2.3 用原则反向索引模式一张选型速查表文档末尾有一节模式选择关键维度我把七条原则和它对齐成一张速查表复习时做反向索引——先闻到代码味道再定位原则最后落到模式家族代码味道对应原则优先候选模式一个类职责重叠、改动频繁SRP策略、命令、状态拆分行为新增功能要改老代码OCP策略、装饰、观察者留扩展点高层直接 new 具体实现DIP工厂方法、抽象工厂注抽象子类覆盖后行为不一致LSP模板方法固定骨架类间直接耦合过深LOD中介者、外观转发/收口继承层级过深、复用困难CARP装饰、组合组合优先接口过大、空实现一堆ISP适配器、代理按需隔离注意这张表是复习索引不是设计方案的标准答案。真实项目里一个模式往往服务两三条原则一条原则也可能对应多个模式最终还是要回到具体场景里权衡。2.4 原则类考题的复习技巧从记定义到给例子文档每章后带的练习题大量是给出场景判断违反哪条原则或者给出原则举一个符合/违反的例子。复习这类题有两个技巧。技巧一是给每个原则准备两个自己的例子一个来自文档比如 SRP 的计算器拆分一个来自手头项目的真实代码比如某个 Service 类既做参数校验又写日志又调外部接口。技巧二是把原则两两组合出题比如OCP 和 DIP 同时被违反的代码长什么样——一个模块里既 new 具体类、又无法扩展基本就是最典型的坏味道。这样复习会从被动背诵变成主动匹配考场上反应快很多。3. 七大重点模式的 Java 落地四个关键场景与必背代码骨架3.1 抽象工厂产品族与产品等级结构的边界抽象工厂是文档第一个重点模式也是和工厂方法最容易混淆的一个。分界线一句话就能讲清工厂方法解决一类对象的创建抽象工厂解决一组相关对象的创建。文档里的数据库访问案例是标准示范——系统要同时支持 SQL Server 和 Access 两套数据库业务对象有 User 和 Department。用工厂方法的话每套数据库要写两个工厂分别创建 User 和 Department客户端得记住哪套库配哪个工厂用抽象工厂SqlServerFactory 一次把 SqlserverUser 和 SqlserverDepartment 都给齐客户端只依赖 IFactory 抽象创建逻辑被收进工厂这个黑匣子。关键代码骨架// 抽象产品用户表操作接口 public interface IUser { void Insert(User user); User GetUser(int id); } // 具体产品SQL Server 实现 public class SqlserverUser implements IUser { Override public void Insert(User user) { System.out.println(在 SqlServer 中给 User 表增加一条记录); } Override public User GetUser(int id) { return new User(); } } // 抽象工厂声明创建一组相关产品的方法 public interface IFactory { IUser CreateUser(); IDepartment CreateDepartment(); } // 具体工厂创建 SQL Server 产品族 public class SqlServerFactory implements IFactory { Override public IUser CreateUser() { return new SqlserverUser(); } Override public IDepartment CreateDepartment() { return new SqlserverDepartment(); } }两个设计要点要读懂。第一客户端拿到的永远是 IUser 和 IDepartment 接口类型不关心底层是 SQL Server 还是 Access这就是依赖倒转原则的落地第二切换数据库时只需要改一行IFactory factory new SqlServerFactory();为new AccessFactory();业务调用代码零改动这是抽象工厂最大的价值。代价同样明确如果新增一个业务对象如 OrderIFactory 要加方法所有具体工厂都要同步修改直接违反 OCP。文档把这条缺点写得明白面试常考复习时别只背优点。3.2 策略模式把 if-else 链拆成可插拔算法策略模式是七种重点模式里最实用、最好上手的一个。文档的商场收银系统例子是教科书级正常收费、满减、打折分别封装成策略Context 持有一个策略引用按需切换。这里有一个容易被带偏的点策略模式的本质是算法的封装与替换不是消除所有 if-else。你仍然需要一个地方决定用哪个策略只是这个决策点被收口了。典型代码// 策略接口所有收费算法的公共抽象 public abstract class CashSuper { public abstract double acceptCash(double money); } // 具体策略正常收费 public class CashNormal extends CashSuper { Override public double acceptCash(double money) { return money; } } // 具体策略满减构造参数是满减门槛和减免金额 public class CashReturn extends CashSuper { private double moneyCondition; private double moneyReturn; public CashReturn(double moneyCondition, double moneyReturn) { this.moneyCondition moneyCondition; this.moneyReturn moneyReturn; } Override public double acceptCash(double money) { double result money; if (money moneyCondition) { result money - Math.floor(money / moneyCondition) * moneyReturn; } return result; } } // 上下文持有策略引用并委托执行 public class CashContext { private CashSuper cashSuper; public CashContext(CashSuper cashSuper) { this.cashSuper cashSuper; } public double getResult(double money) { return cashSuper.acceptCash(money); } }两类参数要知道怎么调。第一类是具体策略的构造参数比如 CashReturn 的满减门槛和减免金额由创建方传入改动只影响策略内部。第二类是 Context 的注入方式构造注入适合策略创建后不变的场景Setter 注入或方法注入适合运行时动态切策略。实战里我一般会在策略接口上加一层简单工厂把根据外部参数创建策略的逻辑收口客户端传个枚举就行不用知道具体类名。这只是常见变体文档里的纯策略模式不包含这层工厂面试时分开讲更稳妥。3.3 外观与适配器一个收口一个翻译外观模式解决子系统接口太散的问题思路就是前台接待客户端不直接面对售后、财务、技术三个部门只找前台一个人内部怎么调度是前台的事。代码上是一个门面类把多个子系统的调用组合成高层次接口。典型场景是下单流程调库存、扣积分、发消息三件事客户端原来要调三个 Service外观类出现后一个 doOrder 就够。适配器模式解决接口对不上的问题你手里的类功能完全够用但方法签名和客户端预期不一致。典型场景是兼容旧接口系统里有老版本 Logger 和新版 Log 接口不想改所有调用方就写一个适配器类实现新版接口、内部持有老实现做方法转发和参数映射。两个模式的区别一句话外观是主动收口、简化客户端适配器是被动翻译、对齐接口。文档把它俩放在重点模式里因为接手旧系统时这两个被提起的频率最高。踩坑点在于别混用外观类内部如果做的是字段映射和方法签名转换它其实在偷偷扮演适配器适配器如果还聚合了多个子系统的调用它又变成了外观。判断标准看动机——是在减少客户端接触面还是在让接口对得上两种动机对应两种实现。3.4 组合、单例、中介者三张特征最鲜明的结构名片组合模式解决部分-整体的树形结构。文件夹套文件夹、菜单套子菜单、HTML 标签嵌套都是典型场景。判断标准很实际数据能不能画成树能画而且叶子节点和容器节点都要被同样的方式遍历就用组合。边界在于叶子节点的 add、remove 方法怎么处理——透明式写法是叶子也保留方法但空实现或抛异常客户端无差别对待安全式写法是干脆把 add、remove 从公共接口拿掉容器单独一个接口。// 组合模式公共接口叶子与容器行为统一 public interface Component { void operation(); // 默认方法提供统一签名叶子节点直接抛异常 default void add(Component component) { throw new UnsupportedOperationException(叶子节点不支持添加); } default void remove(Component component) { throw new UnsupportedOperationException(叶子节点不支持移除); } } // 叶子节点只实现业务操作 public class Leaf implements Component { Override public void operation() { System.out.println(叶子节点业务处理); } } // 容器节点持有子节点列表并统一管理 public class Composite implements Component { private ListComponent children new ArrayList(); Override public void operation() { for (Component child : children) { child.operation(); } } Override public void add(Component component) { children.add(component); } Override public void remove(Component component) { children.remove(component); } }Java 8 之后的 default 方法让透明式实现更干净JDK 7 及以前只能靠抽象类或抛异常。文档里 UML 类图两种写法都画过复习时注意区分。单例模式是被用滥又最容易写错的一个。双重检查锁的正确形态是双判空加 synchronized 加 volatile为什么不能省 volatileinstance new Singleton()在 JVM 里不是原子操作分配内存、初始化对象、引用赋值三步可能被指令重排另一个线程在初始化完成前读到引用就会拿到半成品。这类问题一旦出现就是偶发空指针压测才暴露容易被当成玄学。更省心的替代方案是静态内部类或枚举单例两者天然线程安全文档单例章节的对比表把几种写法的差异列得很清楚。中介者模式是迪米特法则的极端表达多个对象不直接互相引用全部通过中介者转发消息。聊天室、GUI 控件联动都是标准场景。它的代价是中介者类会随交互逻辑膨胀成上帝类所以用的时候要克制——中介者只放协调逻辑具体业务留在各个同事类里这是文档里反复强调的边界。4. 剩余 15 种模式的复习路线六组易混对比与一份速查表四章把 7 种重点模式过完之后剩下 15 种模式不能平铺直叙一个个背得按家族和易混关系组织。先提醒一个文档本身的笔误行为型分类写的是 10 种但模式列表实际列了 11 个名字多了解释器按 GoF 标准行为型就是 11 种复习时对照 GoF 计数即可不影响单个模式的掌握。4.1 创建型补齐简单工厂、工厂方法、建造者、原型创建型分两条线复习。第一条线是简单工厂到工厂方法再到抽象工厂的进化关系// 简单工厂一个方法管所有产品创建 public class SimpleFactory { public static IUser createUser(String dbType) { if (sqlserver.equals(dbType)) { return new SqlserverUser(); } else if (access.equals(dbType)) { return new AccessUser(); } throw new IllegalArgumentException(unknown db type); } }这段代码一眼就能看出问题新增一种数据库类型就要往 createUser 里再加一个 else if这就是违反 OCP 的典型形态。工厂方法把创建什么延迟到子类每个产品对应一个工厂子类解决了扩展问题但类数量翻倍抽象工厂再进一步一个工厂管一族产品。三者的本质是创建逻辑收口程度的递进复习时按这个顺序写一遍代码比背十遍定义都管用。第二条线是建造者和原型。建造者关心怎么一步步创建——组装电脑时 CPU、内存、硬盘必选显卡、蓝牙可选链式调用只填关心的字段工厂关心创建谁两者关注点不同。原型模式关心怎么低成本复制通过 clone 复制已有实例注意 clone 默认是浅拷贝引用类型字段会被共享要独立副本就得手动深拷贝。另外简单工厂不算 GoF 23 种模式之一但大量框架里都是XXXFactory.create(type)这种静态方法形态面试先承认它不在 GoF 列表里、再说是工厂方法的简化版反而加分。4.2 行为型主战场观察者、命令、责任链、状态、模板方法行为型是数量最多、场景题最密集的一类。观察者模式是发布-订阅主题维护观察者列表状态变化时通知所有人Java 的 EventListener、Spring 的 ApplicationEvent 都是标准应用。命令模式把请求封装成对象支持排队、撤销、日志回放编辑器 CtrlZ 就是典型。责任链模式让多个处理器依次尝试处理审批流最直观——初级审批人处理不了就传上级每一环决定是自己处理还是往后传。状态模式和策略模式的类图几乎一样区别全在语义策略的算法由客户端主动选状态的状态切换由对象内部自己驱动。模板方法则是固定骨架、延迟步骤数据导入固定四步打开文件、解析、校验、入库解析和校验各有实现骨架顺序不变。按高频考题排序状态和策略的对比出现率最高观察者其次责任链和命令通常是场景题。复习时给每个模式准备一个手写过的迷你示例比背十遍定义更有用。4.3 结构型扫尾装饰、代理、桥接、享元、访问者、备忘录装饰模式是继承的替代方案不修改原类一层层包装加功能Java IO 的 BufferedInputStream 包 FileInputStream 是标准教材。代理模式和装饰的代码结构几乎一样但意图完全相反代理管访问——延迟加载、权限校验、日志装饰管增强。桥接模式把抽象和实现拆成两个维度各自变化JDBC 接口和各数据库驱动是天然案例。享元模式共享细粒度对象大量相同字符、相同图片的场景用得上核心是区分内部状态可共享和外部状态不可共享。备忘录模式就是后悔药保存对象历史状态用于回滚注意快照存储开销编辑器 undo 栈就是它。访问者模式最绕适用于数据结构稳定但操作频繁变化的场景双分派实现方式容易绕晕短期复习可以放在最后。4.4 六组易混对比一张表分清双胞胎模式文档第四部分的对比是精华整理成复习用速查表对比组核心差异一句话判断策略 vs 状态行为变化的驱动方客户端选策略 vs 对象内部自动切状态观察者 vs 中介者通信方向一对多广播 vs 多对多经中心转发适配器 vs 装饰 vs 代理意图翻译接口 vs 增强功能 vs 控制访问组合 vs 装饰结构目的树形组织部分整体 vs 层层叠加附加功能工厂 vs 建造者关注点创建谁 vs 怎么创建单例 vs 原型实例数量全局唯一 vs 克隆副本踩坑提醒表是复习工具考试拿到场景题先问这个场景的核心诉求是什么再对照表选型顺序不能反。比如报表有多种导出格式核心诉求是可替换算法对应策略而不是状态订单在不同生命周期有不同操作核心诉求是内部状态驱动对应状态而不是策略。5. 避坑指南设计模式复习最容易翻车的五个点刷完一遍文档之后大部分人栽跟头的地方高度集中。这五条是我和身边同事反复踩过的坑每一条都是现象-原因-解决的完整链条复习时对照自查。5.1 UML 类图没看懂就开始背代码现象照着文档类图抄一晚上代码第二天面试让手绘类图完全卡壳箭头方向全靠猜。 原因看图时只看了类名和连线没理解语义——实线三角是继承、虚线三角是实现、空心菱形是聚合、实心菱形是组合、箭头指向依赖方。把图当装饰等于没看图。 解决复习前先花 20 分钟过一遍 UML 符号表然后对着抽象工厂的类图把每条线用中文说一遍ConcreteFactory1 实现 AbstractFactory 接口AbstractFactory 依赖 AbstractProductA 和 AbstractProductB。每条线都能讲清楚才算真正看懂一张图。5.2 策略模式和状态模式混为一谈现象写状态逻辑用了策略模式状态切换散落在客户端 if-else 里写策略逻辑用了状态模式算法选择被内部状态机搞乱。 原因两个模式类图几乎一样都是一个接口 多个实现 一个持有者只记结构不记语义就必然混淆。本质区别在驱动方策略由外部调用方显式选择状态由状态类内部自行流转。 解决动手前先问谁来触发变化。触发点在外部调用方是策略触发点在对象内部是状态。订单状态机是典型的状态模式折扣计算是典型的策略模式。文档在策略模式 vs 状态模式对比里点过这一层复习时把它当必背项。5.3 单例模式的懒汉式只写判空不写锁现象多线程环境下 getInstance 偶尔返回两个不同实例线上出现资源泄漏压测才暴露。 原因if (instance null) { instance new Singleton(); }在多线程下不是线程安全的两个线程同时通过判空就会各自 new 一个。加了 synchronized 但漏了 volatile指令重排又会拿到半初始化对象。 解决双重检查锁加 volatile或者直接用静态内部类/枚举单例。文档把单例的多种写法都列了复习时对比着记每种写法的线程安全属性面试被问为什么用 volatile才能答上来。这一条是面试高频没有之一。5.4 组合模式里叶子节点行为不统一现象遍历树形结构时对叶子节点调用 add 抛异常、对容器节点正常客户端代码里出现一堆 instanceof 判断。 原因组合模式的核心价值就是客户端无差别对待叶子和容器叶子行为不一致意味着模式没起到该起的作用。 解决用透明式写法叶子节点保留 add/remove 方法但空实现或抛 UnsupportedOperationException接口签名统一客户端遍历时不感知差异或者用安全式写法add/remove 只放容器接口里代价是客户端要判类型。文档把两种写法都给了工程上透明式更常见也更符合遍历不感知的初衷。5.5 为用模式而用模式过度设计现象一个只有三个类的模块硬塞工厂加策略加观察者三件套代码量翻三倍同事看不懂要改哪里。 原因刚学完新模式的兴奋期把学了新模式当成必须用上新模式忽略了模式的适用场景。设计模式的本质是解决特定问题没有问题就不需要模式。 解决把每个模式的适用场景和不适用场景一起记。文档里每个模式都写了适用场景和优缺点考试可能不考缺点但实际项目里缺点才是保命信息。我的习惯是代码出现明显坏味道——重复代码、超长 if-else、职责混杂——才引入模式而且优先选最轻量的那个。能用简单接口解决的事不要上抽象工厂。6. 用「模式复盘四步法」验证掌握度不刷题也能自测文档结尾附了练习题库和模拟试卷但我的建议是别只刷题——刷题测的是记忆测不出迁移能力。我自己复习时固定走四步每步都有明确验收标准。第一步关掉文档对每种模式画 UML 类图标清每个角色的职责和箭头语义画不出来的就是没吃透。验收标准3 分钟内画完并口头解释每条连线的含义。第二步为每种模式手写 30 行以内的最小可运行 Java 示例抽象工厂写数据库切换策略写折扣计算单例写线程安全的 getInstance。验收标准编译通过核心行为能演示。第三步做场景匹配测试用文档练习题之外的业务场景判断该用什么模式并写伪代码比如订单在不同状态下有不同操作对应状态模式报表有多种导出格式对应策略模式。第四步最重要读一份框架源码或自己项目的核心类标出里面实际用了哪些模式——设计模式只有和真实代码对照过才算真正内化。拿模拟项目 X简化版订单管理系统的订单状态流转来演示最小示例长这样// 抽象状态类固定状态机的骨架 public abstract class OrderState { protected OrderContext context; public abstract void pay(); public abstract void ship(); public abstract void complete(); } // 具体状态待支付状态由状态类自己决定下一个状态 public class PendingState extends OrderState { Override public void pay() { System.out.println(支付成功进入已支付状态); context.setState(new PaidState()); } Override public void ship() { System.out.println(未支付订单不能发货); } Override public void complete() { System.out.println(未支付订单不能完成); } }这个 20 行示例同时验证三件事状态模式的核心是状态类自己决定下一个状态模板方法思想体现在抽象类固定了一组必须实现的操作再结合文档里中介者的思路状态上下文持有引用完成切换外部调用方不再写 if-else。一个示例串起三个知识点比单背定义高效得多。走完四步你会对看懂了和真掌握的差距非常敏感。我第一次走流程时第一步就暴露了装饰模式和代理模式结构几乎一样、容易把持有同一接口和持有具体类引用搞混的问题。从那以后我每次复习设计模式都强制走一遍四步流程先画图、再写码、再场景匹配、最后到项目代码里找实例这个习惯帮我省掉了大量突击复习时间也让我在重构旧系统时更敢下手。希望帮到你。本文还有配套的精品资源点击获取