
设计模式这个系列在整理的时候我没想到能坚持到完结。最初只是自己在项目里重构时记笔记后来发现很多C#开发者在面试和写业务时都卡在同一个问题上模式看懂了代码里用不出来。这套基于C#的23种设计模式总结就是冲着“能落地”去的。现在23篇全部写完我把整个系列的关键内容、代码要点和踩坑记录汇总成一篇目录方便你按图索骥也方便你直接拿来做知识自检。这个系列适合正在学C#基础的人适合准备面试的开发者也适合那些手头维护着老项目、每天被if-else和巨型类折磨的同学。设计模式不是某个框架的专属概念而是一种代码组织能力。你不需要一次性全部看完按自己的节奏挑最痛的点切入就可以。下面我开始把整套23种模式串起来讲。1. 先把框架搭起来23种模式到底在分类什么1.1 设计模式解决的是“变化”的问题一个项目只要跑上一年需求一定会变。今天日志要写到本地文件明天要传到云端今天订单满减明天要改成打折今天设备用Modbus通讯后天又要换OPC UA。如果代码里到处都是if-else和硬编码每次改动都会牵一发动全身。设计模式的核心思想就是把“变的部分”和“不变的部分”拆开。比如流程不变但流程里的某个步骤经常变就用模板方法算法不变但算法的可选实现很多就用策略对象本身不变但需要动态增加新能力就用装饰器。所以学设计模式之前先建立一种思维习惯看到需求时先问一句“这里未来最可能变化的地方在哪”1.2 创建型、结构型、行为型分别管哪一段GoF提出的23种模式按用途分成三大类这个分类本身就是一张很好的地图。分类数量关注点模式名单创建型5种对象怎么创建单例、工厂方法、抽象工厂、建造者、原型结构型7种类和对象怎么组合适配器、桥接、组合、装饰器、外观、享元、Proxy替身/占位行为型11种对象之间怎么协作责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者创建型模式管的是“new”这件事。不是所有对象都应该在业务代码里被直接new出来有些对象创建逻辑很复杂有些对象需要控制实例数量有些对象需要从现有实例复制这些都属于创建型模式的范畴。结构型模式管的是“组合”这件事。类与类之间、对象与对象之间怎么组织才能做到高内聚低耦合。它不关心对象怎么来的只关心对象怎么搭配。行为型模式管的是“协作”这件事。对象之间怎么传递消息、怎么分配职责、怎么处理复杂的流程。业务系统里最常用的几个模式基本都集中在行为型。1.3 C#里的实现和传统UML不太一样网上大部分设计模式教程默认用Java或者C写示例到了C#这边会发现有些模式被“简化”了。例如迭代器模式C#直接用yield return就能实现编译器帮我们处理了状态机观察者模式语言自带event关键字命令模式WPF里ICommand已经是官方抽象。这不是说C#不需要学设计模式而是说C#给了你更轻量的表达方式。模式的思想没有变变的是实现手段。我写这个系列时尽量每个模式都给出符合C#习惯的写法因为照着Java的重度继承去写C#很多地方反而别扭。2. 23种设计模式逐一拆解C#中的关键点与适用场景2.1 创建型5种“把对象造出来”的套路单例Singleton确保全局只有一个实例。C#里最省心的现代写法是LazyT不要自己写双检锁除非你真的对初始化时机有特殊要求。单例适合配置管理器、日志写入器这类“一个就够”的对象但它也很容易被滥用。全局可变状态会让测试变得很难受能用依赖注入容器管理单例生命周期就尽量不要自己写静态单例。工厂方法Factory Method把“创建某个产品”的职责交给子类。基类定义创建接口子类决定具体new哪个类。在C#里配合泛型和依赖注入比传统UML里的继承写法更灵活。典型场景是不同数据库连接、不同文件格式解析器。比如读取CSV、JSON、Excel三种配置各自有独立的Reader工厂根据配置返回对应实例。抽象工厂Abstract Factory负责创建一组相关的产品保证产品之间能配套。比如一套UI主题需要同时创建按钮、输入框、弹窗如果你做WinForm换肤主题A不能混搭主题B的控件抽象工厂就很合适。很多业务系统里的“服务工厂”其实就是抽象工厂的变体用来保证同一套产品族不会互相拆散。建造者Builder把复杂对象的构造步骤拆开让你能一步步组装。C#里最常见的形态是链式调用比如new HttpClientBuilder().WithTimeout(...).WithHandler(...).Build()。它和工厂的区别在于工厂关注生产什么建造者关注怎么一步步生产。适合构造参数特别多、又不希望对象出现“半初始化”状态的场景。原型Prototype从一个现有对象复制出新对象而不是重新new。C#里MemberwiseClone()只能做浅拷贝遇到引用类型成员要手动深拷贝可以用序列化或手动复制属性。适合创建成本高、但差异不大的对象比如复杂报表模板。你用一套默认模板生成新报表只需要复制后改几个字段。2.2 结构型7种“类和对象怎么组合”的套路适配器Adapter把一个接口转换成客户端需要的另一个接口。开发上位机时最常见Modbus设备返回的字节数组、传感器SDK的数据类型和业务层想要的模型不一致写一个Adapter包一层就能解决。但要注意别把所有第三方调用都堆进Adapter否则它会退化成“上帝类”。桥接Bridge把抽象部分和实现部分拆开让两者可以独立变化。比如一个设备控制类既要支持不同品牌设备又分为基础版和高级版如果不做桥接就可能出现“品牌乘以版本”的类爆炸。C#里桥接体现在接口组合上用组合代替继承能省掉大量排列组合。组合Composite把单个对象和组合对象统一对待形成树形结构。菜单、目录树、权限树、组织架构都适合。C#里基类或接口定义Children集合叶子节点和容器节点实现同一套操作递归处理就完事了。好处是调用方不需要关心当前节点是叶子还是容器代码会清爽很多。装饰器Decorator不改变原始对象动态给对象加功能。C#里最经典的例子是Stream体系BufferedStream、CryptoStream、GZipStream一层套一层。业务上做日志、缓存、重试也适合用装饰器。重点是保持抽象接口一致才能实现层层嵌套。但注意调试时调用链会变长所以每层职责要单一。外观Facade给复杂子系统提供一个简单入口。上位机对接海康视频流、OPC UA服务器、Modbus从站时调用方根本不需要关心底层握手细节全部封装到一个DeviceService里就是外观模式。它不是禁止你访问子系统而是提供一个“默认路径”。日常业务里的很多Service本质上也在做这件事。享元Flyweight靠共享对象减少内存占用。字符串驻留、缓存池、连接池都算享元思想。C#里实现享元要注意对象状态里“内部状态”和“外部状态”的边界共享的一定是不可变部分否则多个调用方会互相污染。比如颜色、字体这类重复创建很浪费的属性做成共享池就合适。Proxy替身/占位模式为另一个对象提供一个替身由替身控制对真实对象的访问。常见用途有延迟加载、访问控制、日志记录。C#里EF Core的延迟加载、远程调用里的引用对象都是Proxy的体现。视频流播放场景里先返回一个轻量替身用户真正点开时才去拉流能省不少资源。2.3 行为型11种“对象之间怎么协作”的套路责任链Chain of Responsibility把请求沿着链条传递直到有人处理。ASP.NET Core中间件就是责任链业务上的多级审批、校验器也很典型。C#里可以用链表或集合保存处理者注意链条顺序和终止条件别让请求“走完一整圈”才发现没人接。命令Command把请求封装成对象让发起请求和执行请求解耦。WPF/MVVM里的ICommand就是命令模式按钮只管触发Command具体做什么由ViewModel决定。队列任务、撤销重做也是命令模式的主场。命令对象的优势是它可以被存储、排队、回放这是普通方法调用做不到的。解释器Interpreter定义一套语法规则并解释执行。C#里自己写解释器不多但表达式树、规则引擎、公式计算都属于这个思路。如果语法规则很复杂建议直接上现成的解析库别硬写。这个模式在23种里偏冷门面试会问概念但实际业务很少从零实现。迭代器Iterator提供统一方式遍历集合不暴露内部结构。C#里foreach和yield return是编译器帮忙实现了迭代器模式日常用得很顺手反而容易忽略它的存在。自己写集合类时实现IEnumerableT就能让调用方直接用foreach也能配合LINQ进行后续查询。中介者Mediator让多个对象通过一个中介交互而不是互相引用。WinForm里多个窗体之间传数据直接互相Hold引用会把耦合搞得一团糟用一个EventBus或Mediator就能解耦。C#里可以用事件聚合器实现也可以引入现成的MediatR库。它适合网状依赖比较严重的模块。备忘录Memento保存对象某个时刻的状态之后能恢复。游戏存档、文档撤回、表单恢复都靠它。C#里可以是快照对象加序列化注意只保存需要回滚的部分字段不要图省事把整个大对象都序列化一遍性能和内存都受不了。观察者Observer当一个对象状态变化时通知所有依赖它的对象。C#里event和委托让观察者特别容易实现INotifyPropertyChanged是WPF/MVVM的地基。最大的坑是事件订阅后忘记注销导致对象无法被垃圾回收后面我会专门讲内存泄漏问题。状态State把对象在不同状态下的行为拆分到独立类中让对象根据状态改变行为。工单系统、订单流程、设备运行状态都很典型。它和策略模式结构很像区别在于策略的算法由外部指定状态的行为往往由状态自己驱动迁移。订单从待付款变成已付款能执行的操作完全不同这就是状态模式的价值。策略Strategy定义一组算法让它们可以互相替换算法变化不影响客户端。业务里最常用比如折扣计算、文件导出格式、校验规则。C#里可以把策略类注册到容器以后加算法不用改调用方只加新实现就行。策略模式是最容易“见效”的模式之一适合作为学习起点。模板方法Template Method父类把流程骨架定死子类只实现可变步骤。数据导入导出模板很典型打开文件、读数据、清洗、写库、发通知顺序固定但不同数据源的解析细节不同。它是“继承钩子方法”的教科书式应用。用virtual标记可扩展步骤子类可以选择覆盖或不覆盖。访问者Visitor在不修改对象结构的前提下给结构中的对象增加新操作。常见于抽象语法树、报表引擎。C#里实现访问者要支持双分派比较绕。如果你的对象结构相对固定而操作经常变可以考虑它如果结构也经常变建议用别的方式否则两边都在变代码维护成本会非常高。3. 从“看得懂”到“用得对”我的学习路径建议3.1 先把C#这几个语言机制吃透设计模式在C#里不是孤立概念。委托、事件、泛型、接口、反射、特性、LINQ表达式树这些东西不熟模式就只能是UML图。比如观察者背后是委托策略经常配泛型工厂常和反射一起按配置创建对象。我建议先复习interface和抽象类的区别再练习委托与事件再研究IEnumerableT和yield然后回来看模式会顺畅很多。很多同学学模式卡住不是模式本身难而是看不懂示例代码里的语言特性。语言基础补上之后模式的学习曲线会瞬间降下来。3.2 用“坏味道”反推模式而不是背图我见过很多人背了UML到项目里还是不知道用哪个。反过来做反而有效当代码出现这些味道再去翻模式表。构造函数一堆参数、创建逻辑乱看建造者或工厂一个if-else或switch反复判断类型看策略、状态或工厂第三方SDK接口很难用看适配器或外观日志、缓存、校验逻辑散落各处看装饰器或模板方法对象之间网状耦合看中介者或观察者创建成本高、重复对象太多看原型或享元。模式不是用来“装点门面”的它是对抗坏味道的工具。代码没有坏味道就不要硬套模式。最理想的状态是当你闻到某个味道时大脑里自动弹出两三种候选模式做一次取舍。3.3 结合真实框架源码去验证学完理论之后最好的验证方式是看框架源码。ASP.NET Core的请求管道就是责任链IHttpClientFactory里有工厂和Builder的影子EF Core的DbSet是仓储门面的实现思路WPF里的ICommand和INotifyPropertyChanged更是把命令模式、观察者模式挂在嘴边。你不需要把全部源码读完挑一个自己天天用的框架断点跟一遍启动流程比看十篇博客都有用。看见框架底层如何处理“变化”你才会明白为什么设计模式是“经验”而不是“规定”。3.4 拿一个上位机项目当综合练习如果你写C#上位机拿“读设备温度并上报”当练习很合适用Adapter统一不同品牌传感器的数据格式用Facade封装Modbus/OPC UA连接细节用Strategy支持TCP、串口、文件三种数据源用Observer把温度变化推给界面和告警系统用Template Method固定“采集-清洗-上报”流程用Singleton管理全局配置。一个项目就能把一半模式过一遍。即使你不做上位机在业务系统里也能把“文件导出”“消息推送”“权限校验”设计成类似结构。这里给一个模板方法的简单例子public abstract class DataImporter { public async Task ImportAsync(string path) { var data Read(path); var cleaned Clean(data); await SaveAsync(cleaned); Notify(cleaned.Count); } protected abstract IEnumerableRow Read(string path); protected virtual IEnumerableRow Clean(IEnumerableRow rows) rows; protected abstract Task SaveAsync(IEnumerableRow rows); protected virtual void Notify(int count) { } }Read和SaveAsync是必须由子类实现的步骤Clean和Notify是钩子方法子类可以选择覆盖。这样以后新增一种导入格式只需要新写一个子类流程骨架不用动。4. 高频面试题与项目避坑实录4.1 面试官最爱问的几个模式题我整理了几个在C#面试里出现频率很高的问题每个问题背后都对应一套考察点。面试题答题要点单例如何保证线程安全LazyT或双检锁注意静态构造函数和beforefieldinit问题工厂方法和抽象工厂的区别工厂方法管一个产品等级抽象工厂管多个产品族策略模式和状态模式结构一样怎么区分策略由上下文决定算法状态由状态自己驱动迁移观察者模式如何避免内存泄漏取消订阅、弱事件模式、或改用消息中介者装饰器和继承扩展功能的差别装饰器用组合避免子类爆炸但调试调用链更长C#里哪些语言特性天然对应设计模式事件对应观察者yield return对应迭代器ICommand对应命令面试时不要只背定义最好能结合自己项目里的真实场景。哪怕是一个很小的例子也比教科书答案有说服力。4.2 最容易“翻车”的四个实战场景**场景1单例变成全局垃圾桶。**一个项目里全是XXXManager.Instance测试没法写依赖关系混乱。对策用依赖注入容器管理生命周期单例里只放不可变配置和线程安全组件尽量不要放可变业务状态。**场景2工厂套工厂代码找不到北。**为了用工厂模式而工厂结果每个具体类型都配一个工厂类调用链巨长。如果只是一个new就能解决的问题直接构造函数注入就够了别硬造工厂。模式是为了简化不是为了增加层级。**场景3事件订阅了不取消。**观察者模式在C#里太方便导致很多人完就忘了。长时间运行的服务里界面反复打开越跑越慢内存不断上涨。对策在Dispose里统一注销必要时用弱事件模式或改成消息中介者。**场景4装饰器套太多出问题难排查。**装饰器虽然灵活但调试时堆栈会很长都不知道是哪一层改坏了数据。对策给装饰器起清晰的名字每个装饰器只做一件事并在关键节点打日志。4.3 “变化点”速查表现场选型不慌在实际设计阶段我习惯先问“这里的核心变化点是什么”然后照着表选型。代码痛点优先考虑模式对象创建逻辑散落工厂方法、抽象工厂、建造者对象构造昂贵原型、享元接口不兼容适配器需要动态加功能装饰器子系统调用太复杂外观、责任链对象间耦合太强中介者、观察者算法经常替换策略状态迁移复杂状态流程固定细节多变模板方法需要记录操作日志或撤销命令、备忘录这张表不是标准答案但能帮你快速缩小选择范围。真正落地时还要考虑团队熟悉度、现有架构、测试难度模式不是越多越好。5. 总目录的使用方式与后续扩展5.1 按什么顺序刷这个系列我建议先按“项目改造”路线学策略、模板方法、观察者、装饰器、外观这几个模式在真实业务里见效最快学完当天就能用。然后是工厂、单例、建造者这类创建型模式。最后再啃状态、中介者、访问者这些相对重型的。如果是为了面试可以按创建型、结构型、行为型的顺序系统过一遍。但不管是哪种路线都不建议一上来就背访问者和解释器这两个模式在普通业务代码里出现频率不高一上来硬啃容易打击信心。先把高频模式用熟练再回头补冷门概念效率会高很多。5.2 把系列文章当成“错题本”每读完一种模式打开自己的老代码问三个问题这里有没有已经出现的坏味道这个坏味道最匹配哪种模式引入模式后是变简单了还是更复杂了如果答案是变复杂就别硬套。设计模式不是装饰品它在代码里的价值应该是让修改点更集中、让调用关系更清晰。如果一个模式引入之后团队里所有人都看不懂那不管书上怎么说它“优雅”都要重新评估。5.3 后续想继续写的方向这个系列完结之后我打算补几个方向模式之间的组合使用、常见反模式、模式在框架源码中的实际落地以及设计模式和领域驱动设计怎么配合。后面还计划写一些“从坏味道到模式”的重构实战把某个乱糟糟的模块一步一步改成可维护的结构。我个人的习惯是每隔半年把以前写的代码翻出来重构一遍很多模式不是一开始设计出来的而是在重构中“长”出来的。23种模式全部写完后最深的体会不是“我学会了多少套路”而是“我越来越敢改代码了”。如果你想验证自己是否真的掌握挑一个你最常写的模块把里面最乱的一段改造成至少能用两种模式解决的版本改完之后你会发现设计模式其实是一种取舍能力。