ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C# 23种设计模式全解析:创建型、结构型、行为型实战总结

C# 23种设计模式全解析:创建型、结构型、行为型实战总结 把23种设计模式全部写完是在上周的事。回头翻这二十几篇文章最大的感受是经历过一遍从概念到落地、从背诵到实战的过程再回头看设计模式会发现它没那么玄也没那么难。这也是我写这篇文章的初衷——给这个系列画个句号同时给你一张可以直接收藏的总目录。这篇文章会按创建型、结构型、行为型三大类把23种模式全部过一遍每个模式讲清楚它解决什么问题、在C#里最常见的写法是什么、哪些坑我替你先踩过了。不管你是在准备面试还是在改一个历史遗留项目或者单纯想看看自己的代码有没有“模式味”这份目录都会对你有用。1. 为什么程序员都该啃一遍这23种设计模式1.1 设计模式解决的是“人”的问题设计模式不是银弹也不是代码规范它是一套被反复验证过的、可复用的解决方案。很多人初学时喜欢背类图觉得记住了UML就是学会了实际根本不是那么回事。模式真正解决的是“沟通问题”你说“这里用了装饰器”别人马上明白你的意图不用一行一行读代码你说“这是个策略模式”审查代码的人立刻知道算法有替换空间。C# 又是很注重“表达力”的语言委托、事件、LINQ 这些特性让很多模式的实现变得非常简洁但前提是你得先会认。这个系列写完 23 种模式之后我更强烈的体会是模式本身不难难的是知道什么时候该用、什么时候不该用。1.2 这份总目录适合谁看这套目录适合三类人刚学完 C# 基础、想进阶的人准备面试、需要系统过一遍知识点的人维护历史项目、看到别人的“奇奇怪怪”设计但看不懂的人。你可以按需查阅也可以顺着创建型、结构型、行为型三个分类从头读。每个模式我都会给出核心思想、C# 写法和实际项目里的使用场景尤其会点出那些“网上说法千篇一律但实际一写就踩坑”的地方。如果你时间有限至少把速查表记住再结合自己项目的代码去对照进步会比闷头刷书快得多。2. 创建型模式五种模式管好对象的“出生”创建型模式解决的是“怎么把对象造出来”的问题。平时一句new很简单可一旦对象构造逻辑变复杂、变多变散直接 new 就会把调用方和具体类死死绑在一起。创建型模式的共同目标就是让对象的创建过程与使用过程解耦调用方不一定要关心对象到底是谁、怎么拼装出来的。C# 里因为有了泛型、委托、反射这些工具创建型模式的写法往往比传统类图更灵活但核心思路没有变。2.1 单例模式全局唯一但别乱用单例模式是最容易解释、也最容易背的保证一个类只有一个实例并提供一个全局访问点。很多教程上来就写双重锁检查但如果你的项目是 .NET 6直接用LazyT是最省心的做法默认线程安全代码也短。public static readonly LazyMyService Instance new(() new MyService());这一行基本就够了。真正要小心的不是写法而是“全局唯一”容易被当成“方便到处取数据”的借口。日志、配置、缓存这类东西适合单例但如果你发现有五六个单例在互相调用差不多该考虑是不是把依赖关系搞乱了。2.2 工厂方法模式把对象的“生产”交给子类工厂方法的核心是“定义一个创建对象的接口让子类决定实例化哪个类”。C# 里最常见的实现是基类提供一个protected abstract的创建方法子类去重写它。比如报表导出功能基类定义好“导出”的完整流程但具体生成 Excel 还是 PDF交给子类决定。这样做的好处是新增一种导出格式你只需要加一个子类不用改调用方的代码。很多新手会把工厂方法和直接switch混淆其实工厂方法强调的是“继承体系内由子类决定”如果只是根据字符串switch出不同对象那更接近简单工厂不是同一个东西。2.3 抽象工厂模式一整套产品的“生产线”抽象工厂是用来创建“一系列相关对象”的它的重点不是单个对象而是一整套产品之间要配套。比如一套 UI 框架按钮、输入框、弹窗这三个控件必须风格统一再来一套暗色主题就得能同时产出暗色风格的三个控件。C# 里通常用一个接口定义三个方法分别返回按钮、输入框、弹窗的抽象类型每个主题类去实现这个接口。相比工厂方法抽象工厂的扩展成本更高因为每加一个产品族所有具体工厂类都得跟着改。所以我在实际项目里只有确定产品族会成套出现时才会用它否则更愿意用简单的工厂方法。2.4 建造者模式拆解复杂对象的构造过程当对象的构造函数参数越滚越多七七八八的配置项加起来十几个写了还容易传错的时候建造者模式就该出场了。C# 里最常见的形态是链式写法new HttpRequestBuilder().WithUrl(...).WithTimeout(...).WithHeader(...).Build()。它的核心不是让你少写几个字而是把“构造过程”和“对象本身”分离让调用方只看得到自己关心的参数。要注意的是建造者模式需要额外维护一套 Builder 类如果对象本身只有三五个参数硬上 Builder 反而增加阅读负担。我自己常用的判断标准是参数超过 6 个或者存在必填/选填组合要求再考虑建造者。2.5 原型模式克隆自己而不是重头 new原型模式是通过复制已有实例来创建新对象适用于创建成本很高、或者想保留对象当前状态的场景。C# 自带ICloneable接口但这个接口有个坑它没有定义到底是浅拷贝还是深拷贝实现全凭自觉。所以很多老手不建议直接依赖ICloneable而是自己在类里定义一个Clone()方法明确注释是浅拷贝还是深拷贝必要时用序列化或手动逐字段复制。原型模式最常见的误用是拿它当“对象复制工具”有些场景直接写构造函数来做拷贝反而更清晰。如果只是偶尔复制一次就别为了“模式”硬造一个 Prototype 抽象。3. 结构型模式七种模式理清对象的“组合关系”创建型管的是对象怎么来结构型管的是对象怎么“组装”。这一组的核心思想是通过类与对象之间的关系把大系统拆成更灵活的小块。在 C# 里接口、继承、组合、泛型这些东西天然为结构型模式提供了很好的土壤。很多人觉得结构型比创建型难记那是因为没找到共同点每个模式其实都在调整“接口”和“实现”的关系。把它理解成乐高积木的接口造型设计就好记多了。3.1 适配器模式老接口也得配上新系统适配器模式解决的是“接口不兼容”的问题你手上有一个老组件接口是旧的但新系统只认新接口又不能直接改老组件源码于是写一层适配器把调用转换一下。C# 里最典型的场景是接入第三方 SDK对方的回调格式和我内部定义的消息类不一样我就包一个 Adapter把第三方 SDK 的输出转成内部标准消息。别小看这个简单封装它能把“别人家的接口”隔离在系统边界之外。常见坑是适配器越写越厚最后把业务逻辑也塞进去了这时候适配器就变成了上帝类反而比不迁就还要难维护。3.2 桥接模式抽象和实现各自独立进化桥接模式解决的是“维度多导致类爆炸”的问题。比如画图形按形状分圆形、矩形按颜色分红色、蓝色如果继承硬搞会有 2x2 个类再加一个颜色或形状类数量立刻翻倍。桥接的做法是把“形状”和“颜色”各拆成一个独立维度通过组合而不是继承连接起来。C# 里实现时通常会让抽象层持有一个实现层的接口引用这样形状和颜色都可以各自扩展而不影响对方。实际业务里凡是发现有“两个维度同时变化”的迹象就可以考虑桥接。它的代价是增加了一层间接调用逻辑上没那么直观。3.3 组合模式让单个对象和组合对象用起来一样组合模式的核心思想是“部分和整体要有一致的操作方式”。最典型的是文件系统一个文件可以“删除”一个文件夹也可以“删除”删除文件夹时会递归删掉里面所有文件但对用户来说都是同一个删除操作。C# 里通常定义一个抽象节点类叶节点和容器节点都实现它容器节点内部持有子节点列表递归调用子节点方法。做权限系统时单个权限项和权限组也可以这样设计。组合模式的坑在于容易把公共方法写得太宽为了照顾所有子类型导致叶节点里堆了一堆没意义的方法得靠抛异常兜底。3.4 装饰器模式给对象动态加功能装饰器模式是用来“在不修改原类的情况下动态添加职责”的。C# 里最直观的例子就是Stream你可以给FileStream套一层BufferedStream再套一层CryptoStream每一层都增强一点能力但外层用起来仍然是Stream。装饰器和继承的区别是继承是编译期静态绑定装饰器是运行时组合。实际写业务时缓存、日志、重试这些横切逻辑都可以做成装饰器。但有个问题容易忽略装饰器会改变对象的“身份”如果用装饰器包装过的类型去和原类型做类型判断结果可能不稳定这点要在设计时想清楚。3.5 外观模式给复杂子系统一个统一入口外观模式很简单当一个子系统内部有十几个类、方法调用关系复杂时对外只暴露一个简单的门面类调用方只需要调用门面上的几个方法就行。比如一个上传功能涉及文件校验、压缩、分片、上传、回调通知五个步骤我不可能让每个业务方都自己拼一遍而是提供一个UploadService.UploadFile()把整条流程封装起来。外观模式的代价是门面类容易逐渐膨胀最后变成“上帝服务”。我的习惯是门面只做流程编排不做具体逻辑具体逻辑仍然放在各模块内部这样门面永远只是薄薄的一层。3.6 享元模式共享内在状态节省内存享元模式的目标是“用共享减少对象数量”适合存在大量相似对象、且每个对象可以拆出“不变的内在状态”和“变化的外在状态”的场景。最经典的例子是文字编辑器里的字符对象每个字符的字体、字形是固有属性可以共享坐标、颜色是外部状态由外部维护。C# 里可以配合工厂来管理对象池客户端传入外部状态来获取共享实例。现在很多业务系统内存都够大反而容易忽视这个模式但如果你写的是游戏、图形渲染或报表这种大量重复对象的程序享元模式依然很值得用。3.7 代理模式不是本尊但能帮你挡事代理模式是给目标对象提供一个代替者由这个代替者控制访问。代理和装饰器结构很像但意图不同装饰器重点在“增强功能”代理重点在“控制访问”。C# 里很常见的是懒加载代理一个大型对象创建代价高先用一个轻量代理占位等到真正访问时才创建真实对象。还有人用代理做权限校验比如只有管理员才能执行某个方法。日常开发中谈到EF Core的延迟加载、Castle DynamicProxy这类库本质都是在做代理。需要注意代理会增加调用链长度如果代理里塞了太多逻辑出了问题排查起来会特别痛苦。4. 行为型模式十一种模式掌握对象间的“协作方式”行为型模式是三类里数量最多的也是最贴近业务逻辑的一类。前面创建型、结构型主要管“对象长什么样、跟谁搭配”行为型管的是“对象之间怎么通信、怎么分配职责”。这一组最容易让人记混因为很多模式看起来都是“把一个流程抽出来”。我的学习方法是抓每个模式最核心的动词模板方法定骨架策略换算法观察者发通知责任链转手命令做封装状态管切换。把这个动词抓到模式就不会认错。4.1 模板方法模式把算法骨架留给基类模板方法模式是在基类里定义好算法步骤把某些步骤延迟到子类实现。比如做订单校验先查用户再查库存再冻结金额这三个步骤骨架固定但“查库存”在不同的场景下可能走不同仓库那就让子类去重写这一步。C# 里用抽象方法或虚方法来实现留白即可。这种模式的优点是复用骨架、约束流程缺点是继承关系让子类和基类耦合得很紧。我见过很多团队把模板方法做成三层基类最后改一处流程要牵动一堆子类所以建议基类层级尽量保持浅平。4.2 策略模式把算法换成一组可替换的策略策略模式的核心是“定义一系列算法把它们各自封装起来并且可以互相替换”。C# 里最典型的就是排序函数传入IComparerT比较规则不同排序结果就不同。业务中我常用来处理运费计算不同物流公司有不同计费策略每个策略一个类都实现同一个接口前端传入物流公司编码后端从容器里取出对应策略执行。策略模式能有效干掉大段的 if-else但不要为了消 if-else 而不顾实际如果只有两三种散落的变化写多个策略类反而显得笨重。4.3 观察者模式发布/订阅的经典实现观察者模式定义一种一对多的依赖关系当一个对象状态变化时所有依赖它的对象都收到通知。C# 里最自然的实现是event和delegate声明一个public event EventHandlerOrderCreatedEventArgs OrderCreated;业务模块里订阅它事件发生时就自动触发。这个模式在 C# 里用起来太顺手了但顺手带来的问题是容易过度使用到处都是事件调用链变成“散弹式”非常难调试。我的经验是事件跨类、跨层时一定要命名语义清晰参数对象不要裸传一堆零散字段最好封装成事件参数类。4.4 迭代器模式用统一方式遍历集合迭代器模式让客户端可以用统一的方式顺序访问集合内部元素而不暴露集合的底层结构。C# 开发者的幸福之处在于语言直接把迭代器做到了底层foreach就是迭代器模式的语法糖yield return更是让你可以方便地写自定义迭代器。用迭代器模式最大的价值是“延迟执行”配合yield return集合元素可以按需生成处理大型数据流时不会一次性加载到内存。比如读取超大文件逐行yield return返回内容性能会好很多。很少有人会刻意写一个IEnumeratorT类但理解迭代器原理对排查foreach中的迭代器异常很有帮助。4.5 责任链模式一个请求一路传递责任链模式把多个处理对象串成一条链请求沿着链传递直到某个对象处理为止。C# 里最常见的是 ASP.NET Core 的中间件机制请求先经过认证、日志、静态文件等中间件一层层传入每一层都可以决定继续传递或短路返回。业务中做审批流也很适合不同级别审批人组成链上一级不通过就拦住。责任链的好处是解耦了“谁处理”和“怎么组织处理链”坏处是链条一长调试时容易看不清请求到底停在哪一环。所以实现时最好给每个环节加清晰的名字和日志方便定位。4.6 命令模式把请求封装成对象命令模式把“执行某个操作”封装成一个对象这样请求发送者和执行者之间就不再直接耦合。C# 里用Action和FuncT其实是命令模式的简化版但传统写法通常会定义一个命令接口包含Execute()和Undo()。命令模式适合做操作队列、撤销重做、宏命令等功能。我写过一个小型编辑器每个编辑操作都是一个 Command 对象用栈保存执行记录撤销时弹出栈顶调用Undo()效果非常直观。坑在于撤销逻辑本身就很难写一个命令的Undo()如果不能完全恢复现场这个模式会变成事故源头。4.7 状态模式对象状态变行为跟着变状态模式让对象在内部状态变化时改变自身行为看起来就像换了类一样。C# 里可以定义状态接口把不同状态下的行为拆到不同状态类中上下文对象持有当前状态调用时委托给状态类处理。最常见的例子是订单状态机待支付、已支付、已发货、已完成不同状态下同一操作比如“取消”的服务逻辑完全不同。用状态模式能把一堆if (state ...)的代码拆干净。不过状态模式使用门槛不低状态之间的流转关系如果复杂很容易出现状态类互相跳转的“网状依赖”建议画好状态转换图再动手。4.8 解释器模式定义一套小语言的语法规则解释器模式是用来定义一套语法规则并解释执行这条语法对应的语言。平时业务代码里很少会手写解释器但你用过的正则表达式、模板引擎、表达式求值库底层都有解释器模式的身影。C# 里如果想做一个简单的表达式解析比如“SKU 编号规则解析”可以用解释器把输入拆成语法树节点定义每个节点的解释方法。这种模式最大的特点就是容易扩展也容易掉进性能坑如果语法规则本身很复杂解析器可能非常慢必须配合缓存或优化。我的建议是除非你真的在写语言、引擎、规则解析器否则尽量找现成库别自己实现解释器。4.9 中介者模式让对象之间不直接对话中介者模式用一个中介对象来封装一组对象之间的交互让对象之间不直接互相引用。典型类比是聊天室每个人不直接给所有人发消息而是把消息发给“聊天室服务器”由它转发。C# 里做 UI 程序时多个窗体控件之间互相联动如果每个控件的值变化都直接去修改其他控件依赖会非常乱引入一个中介者统一处理联动逻辑窗体本身会清爽很多。现在很多团队用 MediatR 这类“中介者模式”的库来解耦业务请求虽然名称一样但场景更偏向命令查询分离本质思想还是“别让对象互相直接说话”。4.10 备忘录模式把状态存个快照备忘录模式用于保存对象的某个时刻状态以便后面可以恢复。C# 里要保存状态通常有两种做法一种是定义只读的 Memento 类私有字段不接受外部修改另一种是直接把状态序列化成 JSON/二进制存起来。做撤销功能、草稿箱、游戏存档这类功能时非常合适。坑在“快照能不能做深拷贝”如果对象里嵌套了集合或其他引用类型保存的 Memento 必须彻底复制否则你存了一个引用后面原对象改了快照内容也跟着变。很多人学这个模式时觉得简单用的时候才发现浅拷贝会造出各种灵异 bug。4.11 访问者模式在不改类的前提下增加新操作访问者模式让你可以在不修改已有类的情况下给一组类增加新操作。做法是定义一个访问者接口里面为每种元素类准备一个重载方法元素类提供一个Accept(visitor)访问者再根据具体元素类型执行对应逻辑。C# 里常见的应用场景是对 AST 语法树做不同处理分析、格式化、生成代码每个处理逻辑写成一个访问者。之所以不常被推荐是因为引入访问者会让类结构变得绕而且新增一种元素时所有访问者接口都得跟着改。除非你明确要针对一组稳定对象不断扩展操作否则建议慎用。5. C#里那些“长得不像模式”的模式GoF 的 23 种模式是通用面向对象设计但到了 C# 里语言本身的特性让一部分模式有了“更地道”的表达方式。有时候你已经在用某个模式只是没有意识到。理解这些“隐形模式”能帮你把语言特性和设计思想串起来。5.1 委托和事件观察者的原生形态event和delegate不但在语法上支持观察者模式而且比手写订阅者列表更安全。普通观察者模式需要在主题对象里维护一个订阅者集合而 C# 的事件关键字帮你处理好了加减订阅、空判断这些细节。使用事件时有个容易忽略的点事件是在发布者线程上同步触发的如果订阅者方法耗时长发布者会被卡住。所以大型系统里经常把事件触发放进消息队列或后台任务这和观察者模式本身无关但现实工作里经常要处理同样的线程问题。5.2 LINQ 和 yield迭代器模式无处不在写了多年 C# 的人几乎每天都用foreach和 LINQ但未必意识到这背后就是迭代器模式。IEnumerableT就是迭代器接口yield return实现了惰性求值的枚举器。正是因为有这层设计Where(...).Select(...)才能做到“等你要结果的时候才真正执行”。理解这一点你就明白为什么 LINQ 查询不能随意多次枚举一个IEnumerableT可能是一次性的数据流第二次遍历可能得到空集合或重新执行副作用。处理大数据时这种认知能避免不少隐蔽的性能问题。5.3 using 与 IDisposable把清理动作做成模板方法using语句本质上是try-finally的语法糖而如果把Dispose()看成算法流程中留白的那一步它就很像模板方法模式。很多库的设计也印证了这一点从Stream到DbContext都要求使用者通过using来保证资源的释放。在自建类时实现IDisposable不只是为了“销毁资源”更是为了给调用方一个约定你用完了我的对象就必须触发这个清理流程。设计时还要记得“可空释放”与“幂等释放”的问题Dispose()被调用两次也不应该抛异常。5.4 扩展方法与依赖注入模式与语言的碰撞扩展方法可以让一个类在不被修改的情况下增加新功能行为上很像“装饰器”的简化版但它本质是静态方法无法做到运行时的动态状态管理。依赖注入则经常用来实现工厂模式和策略模式容器根据接口配置自动装配出对应实例这就省去了手写工厂类的繁琐。C# 生态里这种“模式被语言重构”的例子越来越常见写代码时没必要强行套 GoF 类图只要抓住了模式想解决的核心矛盾用最地道的语言特性实现它往往更加优雅。6. 实战中的模式运用怎么用才不会用力过猛模式学完很容易出现一个极端看什么都像模式恨不得每个类都套上“身世”。说实话这种代码比不用模式的还难维护。这里分享一些我踩过坑之后总结的经验。6.1 从坏味道反推模式正确姿势是“闻到坏味道再找模式”而不是“先找模式再安排类”。比如看到大量 if-else 在判断类型可以考虑策略模式或工厂模式看到某个类改了行为导致一堆调用方受影响可以考虑观察者模式或中介者模式看到构造函数参数长到头疼可以考虑建造者模式。模式是被需求逼出来的不是被设计“设计”出来的。我在代码评审里最常给新人的建议就是先写符合直觉的代码等确实出现重复、耦合、扩展困难时再用模式去重构而不是第一版就把所有可能的变化全部抽象化。6.2 一个真实的组合案例很多项目不是只用一种模式而是几种模式组合在一起。我做过一个多渠道支付对接支付渠道有微信、支付宝、银联每个渠道的请求参数、验签、回调处理都不同。这里我用工厂模式根据渠道编码创建支付服务用策略模式封装每个渠道的“下单”和“回调”差异再用模板方法把“记录日志、落库、通知业务方”的公共流程放到基类让子类只实现渠道特有的部分。最终调用方看起来非常干净新增一个渠道也只需要新增一个类再注册到工厂里。这个案例想说明模式组合时每个模式负责一个维度的变化不要一个类里同时承载多种模式职责。6.3 常见问题与排查技巧这里整理几个我实际遇到过的“模式翻车现场”做成速查表症状可能原因建议单例对象在并发下数据错乱单例里存了可变的实例状态单例尽量保持无状态或把状态放到方法参数里工厂方法越来越多分支每个分支只是参数不同没有真正行为差异考虑改用普通方法 参数别硬套模式装饰器套了太多层堆栈很深每一层都拷贝了公共逻辑检查装饰器是不是只做横切关注点公共逻辑应下沉事件订阅后对象无法被垃圾回收事件源持有订阅者的强引用使用弱事件模式或在适当时机显式取消订阅责任链请求没被处理链尾没有默认处理节点增加“兜底处理器”至少记录未处理事件状态模式到处跨状态跳转状态流转规则散落在各个状态类抽出一个状态流转表统一集中管理7. 23种模式速查表7.1 一张表记住 23 种模式为了方便查阅我把 23 种模式按分类整理成表每个模式配一句最核心的适用场景。这张表用于面试前快速回忆也适合贴在公司项目文档首页。分类模式一句话场景创建型单例全局只需要一个实例创建型工厂方法子类决定创建哪种对象创建型抽象工厂需要创建一整套配套产品创建型建造者对象构造参数多、配置复杂创建型原型通过复制现有对象创建新对象结构型适配器接口不兼容需要转接结构型桥接多个维度独立变化避免类爆炸结构型组合单个对象和组合对象要统一对待结构型装饰器动态给对象增加额外功能结构型外观给复杂子系统提供简化入口结构型享元大量相似对象需要共享节省内存结构型代理控制访问、延迟加载、权限拦截行为型模板方法流程骨架固定部分步骤可变行为型策略同一行为有多种可替换算法行为型观察者对象状态变化通知其他对象行为型迭代器统一方式遍历集合行为型责任链多个处理者依次尝试处理请求行为型命令把操作封装成对象便于记录撤销行为型状态状态不同行为不同行为型解释器解析并执行自定义语法规则行为型中介者对象之间减少直接耦合行为型备忘录保存对象状态支持恢复行为型访问者不修改类的前提下增加新操作7.2 怎么用这张表这张表不是给你背的而是给你定位的。遇到具体问题时先从“场景”列找到候选模式再回头翻对应的详细文章看它的类图、C# 实现和注意事项。更好的用法是每次你写完一段代码尝试在表里找一找这段代码是否无意中实现了某种模式如果有再判断是有意设计还是“模式味”冒出来了。记住表里 23 个词只是索引真正有价值的是它背后解决的那类问题。8. 后续还能怎么学8.1 学习顺序建议如果让你从零开始补模式不建议从抽象工厂或访问者这种高门槛的开始。我推荐按“单例 - 工厂方法 - 策略 - 观察者 - 装饰器 - 模板方法”的顺序先上手这六个模式最常用也最容易在真实代码里找到切入点。等这些熟了再碰抽象工厂、建造者、责任链、状态这类偏重流程的模式。命令、备忘录、中介者这类可以按需学面试遇到时突击一下也能应付。最后再用组合模式、桥接模式、解释器模式、享元模式这种“更偏架构设计”的模式来扩展视野。顺序不是标准答案但能让你更快获得正反馈。8.2 三个刻意练习方法第一重构练习从旧项目里挑一个被 if-else 搞乱的类尝试用策略或工厂模式重写然后用测试保证行为不变。第二画图比照把每个模式画成自己的简化 UML但不要照抄书而是画你项目里能对应的例子。第三讲给别人听能用自己的话把模式讲明白才说明真的理解。我每写完一篇设计模式文章就会做一次“给同事讲一遍”的自我测试讲的时候卡住的地方通常就是还没吃透的地方。这套目录只是一个起点真正的收获还是在你自己那一行行代码里。
返回列表