1.总结
1.1 基础知识
学习过的24种设计模式
24种设计模式分为三种类型:创建型模式、结构型模式和行为型模式。
创建型模式:这些模式处理对象创建机制,试图以一种方式来创建对象,以便能够在系统中提供更大的灵活性和可复用性。创建型模式包括:
- 简单工厂模式(Simple Factory Pattern)
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 原型模式(Prototype Pattern)
- 建造者模式(Builder Pattern)
结构型模式:这些模式处理对象组合,试图以一种方式组合类或对象来形成更大的结构。结构型模式包括:
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
行为型模式:这些模式处理对象之间的通信,试图以一种更优雅和有效的方式来分配职责。行为型模式包括:
- 职责链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 策略模式(Strategy Pattern)
- 模板方法模式(Template Method Pattern)
- 访问者模式(Visitor Pattern)
其他常见的设计模式
依赖注入模式(Dependency Injection Pattern):用于解耦组件之间的依赖关系,通过将依赖关系从代码中移动到配置中或使用特定的框架来实现。领域驱动设计模式(Domain-driven Design Patterns):这些模式是为了应对复杂业务场景而设计的,例如实体、值对象、聚合根等。并发模式(Concurrency Patterns):用于处理并发编程中的各种问题,例如锁、信号量、并发数据结构等。异步模式(Asynchronous Patterns):用于处理异步编程中的任务管理、结果处理等问题,例如回调、Promise、Future等。消息传递模式(Messaging Patterns):用于构建分布式系统中的消息传递机制,例如消息队列、发布-订阅模式等。
这些模式都是为了解决特定领域中的问题而设计的,使用时需要根据具体场景进行选择和应用。
1.2 面试题精选
基础题
1. GoF 三类模式各自解决什么问题
题目
简述创建型、结构型、行为型三类模式各自关注的矛盾是什么。
深入解析
- 创建型:对象 如何被创建、谁控制生命周期、怎样在少改调用方的前提下换实现或控制实例个数。
- 结构型:类或对象 如何拼成更大结构,在少动核心逻辑的前提下做适配、扩展、组合、对外简化接口。
- 行为型:对象之间 如何协作与分配职责,把算法、请求、状态变化、通知链等从硬编码里拆出去。
答题示例
三类是按问题域划分的。
创建型对应 new 谁、怎么 new、谁来 new;结构型对应 怎么组装 才对外简单、对内可扩展;行为型对应 运行时谁通知谁、谁执行哪段流程,把易变逻辑从硬编码调用里拆出去。
参考文章
- 1.GoF设计模式概述
2. 单例的饿汉、懒汉、双重检查锁怎么选
题目
各有什么线程安全与资源占用特点,Unity 里 MonoBehaviour 单例还要注意什么?
深入解析
- 饿汉:静态字段在类型初始化时赋值,CLR 保证初始化线程安全;可能提前占用资源。
- 懒汉(无锁):首次访问再
new,多线程下可能得到多个实例。 - 双重检查锁:外层的空判断减少锁竞争,内层在锁内再判空并创建,兼顾懒加载与并发。
- Unity:生命周期跟场景与
DontDestroyOnLoad走;Awake里要做重复实例销毁;查找场景实例的 API 随版本更替,应使用当前文档推荐的查询方式。
答题示例
先确认是否需要懒加载、是否存在多线程竞争。
启动阶段即可创建、希望实现简单:饿汉。延迟创建且有多线程:双重检查锁等线程安全懒式实现。Unity 下
MonoBehaviour单例不走普通构造,一般在 Awake 判重、Find或动态建空物体挂脚本 上保证唯一,并结合场景切换考虑是否DontDestroyOnLoad。
参考文章
- 2.创建型模式-单例模式
3. 外观模式 Facade 解决什么问题
题目
外观是不是「再包一层封装」?和随便写一个工具类聚合调用有什么区别?
深入解析
- 意图:对一组协作的子系统类型提供稳定、面向用例的入口,降低客户端对子系统拓扑与顺序的感知。
- 和乱聚合工具类的差别:门面应对应清晰的子系统边界,内部实现可整体替换;若只有静态方法、职责不清,容易变成全项目共用的「杂物间」。
- 风险:单个 Facade 承接所有入口会膨胀;可按用例或子域拆成多个门面。
答题示例
门面是子系统对外的稳定入口,不是无边界地堆静态工具方法。
它把多步子系统调用收成几条业务流程,调用方只依赖门面。子系统内部重构时,优先在门面内收敛修改面。门面过大就按场景拆分。
参考文章
- 2.结构型模式-外观模式
4. 观察者模式用接口列表和用 C# 事件各有什么取舍
题目
什么时候用 List<IObserver> 遍历通知,什么时候用 event?
深入解析
- 接口 + 列表:类型明确,可封装注册 / 反注册 / 遍历;适合需要自定义通知语义或与非 .NET 回调风格对齐时。
- 事件 / 委托:语法简洁,多播内置;要注意
-=注销、生命周期(Unity 里OnDestroy取消订阅)避免悬挂引用。 - 系列正文也提到:工程里委托实现更常见,但大型框架有时会再包一层主题接口便于测试与扩展。
答题示例
两种做法都能实现一对多通知,选型看团队习惯和框架约束。
列表便于在通知前后插入统一逻辑,测试时也好替换假观察者;
event写法省事,但要保证成对注销。Unity 里组件OnDestroy务必取消订阅,避免已销毁对象仍挂在委托上。
参考文章
- 3.行为型模式-观察者模式
进阶题
1. 「难度 × 频率」排行榜能不能当作学习路线
题目
设计模式学习顺序是否应按网上排行榜来?这类表的价值和局限是什么?
深入解析
- 价值:给新人一个 机会成本 视角,先掌握高频且曲线不陡的模式,更容易在真实项目里见到正反馈。
- 局限:难度与频率来自 抽样与作者主观权重,游戏客户端、后端服务、工具链热点并不相同;有些模式全局排名低,却在当前栈里绕不开。
- 工程说法:排行榜当 选课参考,不当 能力标准;岗位技术栈里反复出现的协作方式,比全局排名第几更该优先吃透。
答题示例
排行表只作参考,不会当作唯一学习顺序。
用它估投入产出:优先啃当前项目里会碰到的。统计口径随领域而变,客户端、后端、工具链热点不一致。结合 耦合点、扩展点、对象生命周期 选型,比背名次有意义。
参考文章
- 1.GoF设计模式概述
2. 简单工厂和工厂方法差在哪
题目
加一种新产品时,两种写法改动面分别在哪里?
深入解析
- 简单工厂:创建逻辑集中在一个类的分支里,新产品常要改既有工厂方法,扩展成本落在修改旧代码上。
- 工厂方法:把「创建」推到具体工厂子类,新产品对应新工厂类 + 新产品类,客户端依赖抽象工厂与抽象产品,旧类可不动。
- 选型:产品类型少、变化少简单工厂够用;产品族持续膨胀、希望符合开闭原则时用工厂方法。
答题示例
差别在变化点落在谁身上。
简单工厂动分支;工厂方法通过新增类型扩展。补充:简单工厂不在 GoF 二十三式正式名单里,工程里仍很常见。
参考文章
- 3.创建型模式-简单工厂模式
- 4.创建型模式-工厂方法模式
3. 桥接模式在解决哪种「类爆炸」
题目
两个独立变化维度时,为什么不用多重继承而用桥接?
深入解析
- 问题形状:形如「平台 × 功能」或「抽象 UI × 皮肤实现」,若用继承枚举所有组合,子类数乘积增长。
- 桥接做法:抽象侧与实现侧各成独立层次,抽象组合实现接口引用;扩展新平台或新功能主要是加类 + 改组合,不是
N×M个子类。 - 原则:合成复用优于为每种组合建子类;与策略思想接近,但桥接强调抽象与实现双维度的长期拆分。
答题示例
两个维度各自会变,用继承会变成笛卡尔积子类。
桥接让抽象侧只依赖实现接口,替换实现即可换平台或换资源形态。手机跑不同系统、英雄换皮肤等都属于这一类拆分。
参考文章
- 6.结构型模式-桥接模式
4. 状态模式和策略模式看起来都像「换实现」,差别在哪
题目
都是一个上下文里换引用,面试里怎么一句话分开讲?
深入解析
- 策略:上下文通常不关心当前是哪种策略,调用方或配置注入不同
Strategy完成同一类任务(如计费规则)。 - 状态:上下文知道自己处在哪一状态;状态对象里常根据输入迁移到下一状态,行为随状态机而变(如连接态 / 断开态)。
- 代码气味:若分支主要在描述「对象现在处于生命周期哪一段」,偏状态;若分支只是在选「同一接口下的不同算法」,偏策略。
答题示例
策略侧重同一任务下换算法实现;状态侧重对象在生命周期各阶段行为不同,且常伴随显式迁移。
状态转换多写在状态类或上下文里,形成状态机;策略一般不强调固定的状态迁移图,而是由外部按需注入实现。
参考文章
- 2.行为型模式-策略模式
- 7.行为型模式-状态模式
深度题
1. 「基础—标准代码—C#—Unity」四段结构对团队文档的意义
题目
如果团队要求复杂模块按「基础概念、标准结构、C# 示例、Unity 实践」写设计说明,利弊各是什么?
深入解析
- 利:促使把 抽象角色、稳定边界、可替换实现 写清楚,新人能按固定套路定位;Unity 一段能把 生命周期、序列化、Inspector 暴露 等引擎侧问题写进设计,而不是停在教科书类图。
- 弊:小改动也套四段会 文档膨胀;示例与真实架构脱节时,容易变成形式主义。
- 折中:对 跨模块协议、长期演进的核心系统 用全四段;局部重构用其中一两段即可。
答题示例
四段本质是从抽象到落地的检查清单。
公共模块、多人协作边界上收益大:词汇统一、接口边界清楚。小需求不必四段写满,否则文档比代码还难维护。Unity 一段应写清 生命周期、序列化、场景与预制体关系,与教科书控制台示例区分开。
参考文章
- 1.GoF设计模式概述
2. 抽象工厂和建造者都在「造一堆东西」,怎么区分
题目
什么需求更该上抽象工厂,什么需求更该上建造者?
深入解析
- 抽象工厂:强调产品族——一次拿到彼此兼容的一组对象(多接口、多实现,成套切换),扩展新族往往牵动多个产品类与工厂方法。
- 建造者:强调同一复杂对象的装配过程——
Director管步骤顺序,各Builder管每步怎么填;换表示或微调流程时动建造者或Construct。 - 合一说法:抽象工厂回答「哪一套」;建造者回答「按什么顺序拼出一个」。
答题示例
抽象工厂是成套换皮肤、换配置档;建造者是分步拼一个复杂体。
例:换整套 UI 主题像抽象工厂;拼关卡数据、拼角色属性链像建造者。两者都会创建多个对象,但意图不同:一个强调成套族,一个强调装配步骤,记类图时要把这点带上。
参考文章
- 5.创建型模式-抽象工厂模式
- 7.创建型模式-建造者模式
3. 享元的内部状态与外部状态,和 ScriptableObject 怎么类比
题目
享元工厂里同一个 key 为什么必须返回同一实例?运行时状态该放哪?
深入解析
- 内部状态:与 key 绑定、可安全共享、一般不变;工厂字典保证同 key 同对象,才能真省内存。
- 外部状态:随调用场景变化,通过方法参数传入,不进共享对象的可变字段,否则多使用者互相踩。
- ScriptableObject:多组件引用同一份资产时,类似共享内部 / 配置数据;若把战斗中的血量、随机种子写进 SO,就变成全局可变单例,和享元初衷相反。
答题示例
享元共享的是不变或极少变的那块;变的都当参数传。
ScriptableObject 适合放共享配置;运行时可变状态应放在实例或组件上,避免写回共享资产。工厂按 key 复用实例,才能保证内存上真的是「一份数据多处引用」。
参考文章
- 8.结构型模式-享元模式
4. 访问者模式为什么说「对操作扩展开放、对元素类型扩展不友好」
题目
Accept + Visit 双分派解决了什么问题,又带来什么维护成本?
深入解析
- 稳定元素、多变操作:元素类只实现
Accept,新报表 / 新校验写成新 Visitor,原有Circle/Rectangle可少改。 - 新增元素类型:通常要在 IVisitor 上增加重载,并修改每一个具体访问者实现,否则编译不过——开闭原则向操作侧倾斜。
- 适用边界:对象结构长期稳定、操作频繁增删(编译器 AST、复杂 UI 树导出)才划算;否则表驱动或
switch模式匹配可能更简单。
答题示例
双分派把「对某类元素做什么」收敛到 Visitor 的一个重载里,调用关系一目了然。
每新增一种元素类型,往往要改 Visitor 接口及全部实现,改动面大。元素类型仍在快速膨胀时,访问者模式要慎用,可评估表驱动或语言自带的模式匹配等替代方案。
参考文章
- 11.行为型模式-访问者模式
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com