设计模式的溯源:从建筑学到软件架构的百年传承
如果你在过去的十年里参加过任何一场互联网公司的技术面试,大概率会被问过这样一个问题:“请你说出单例模式的实现方式,并说明它的适用场景”。如果你恰好背下了《设计模式:可复用面向对象软件的基础》里的23种模式,可能还会顺带答出工厂方法、观察者、策略模式的异同,甚至能画出它们的UML类图。但很少有人会追问一句:这些被无数工程师奉为圭臬的“模式”,到底是从哪来的?为什么刚好是23种?是谁在什么背景下总结出了这些“最佳实践”?我们今天张口就来的“设计模式”四个字,背后其实藏着一场横跨建筑学与软件工程、历时半个世纪的认知迁徙——而它的起点,不是硅谷的车库,也不是某所大学的实验室,是20世纪60年代加州大学伯克利分校校园里,一位建筑师对“好设计”的执念。
---
一、被遗忘的建筑学先声:亚历山大的“模式语言”
1963年,刚满30岁的克里斯托弗·亚历山大站在伯克利分校的建筑学院讲台上,看着台下学生交上来的设计作业,眉头越皱越紧。那些作业的图纸精美得像是印刷品:对称的立面、流畅的动线、符合黄金比例的窗墙比,可当他问起“你设计的这个社区里,老人小孩有没有地方晒太阳?上班族下班了想喝杯咖啡有没有地方歇脚?”时,几乎所有的学生都答不上来。
亚历山大的困惑不是个例。整个20世纪中期的建筑教育都陷入了一个怪圈:教给学生的是“画出好看的图”,而不是“解决真实的问题”。无数的现代化社区拔地而起,楼越建越高、路越修越宽,可住在里面的人却越来越不舒服:街道上没有可以停留的公共空间,邻里之间老死不相往来,孩子出门玩耍要穿过车水马龙的主干道,想找个街角咖啡馆坐一会儿,要走十几分钟才能到最近的商业中心。
亚历山大突然意识到:建筑设计的经验从来没有被真正沉淀下来。以前老师傅带徒弟,靠的是口耳相传的“经验”,比如“街角要留个敞的地方”“窗户不能对着邻居的厨房”,但这些经验从来没有被系统整理成可复用、可传播的知识。如果能把建筑领域反复出现的问题和对应的解决方案总结成一套“语言”,是不是就能让更多的设计师不用再踩同样的坑?
这个想法一放就是十年。从1963年到1977年,亚历山大带着团队走遍了美国、欧洲、亚洲的上百个城市,调研了上千个社区、街道、建筑,记录下了每一个“让人舒服”的设计细节:为什么意大利小镇的街角咖啡馆总是人来人往?为什么日本京都的老巷子让人走起来不想离开?为什么北欧的社区里孩子能自己在楼下玩一整天?他把这些反复出现的问题和对应的解决方案整理成了一套完整的体系,取名叫“模式语言(Pattern Language)”,并在1977年出版了上下两册的《建筑模式语言》[^1]。
书里一共总结了253个建筑模式,每个模式的结构都出奇的一致:首先是问题背景,描述这个模式要解决的具体场景;然后是解决方案的核心,给出可落地的设计原则,而不是死板的图纸;最后是约束条件,说明这个方案适用的边界,什么时候可以用,什么时候不能用。比如大家最熟悉的“街角咖啡馆”模式,它的描述是这样的: > 问题:城市里缺乏非正式的公共交往空间,人们下班后没有地方歇脚、聊天、偶遇熟人,社区的归属感越来越弱。 > 解决方案核心:在每条人流量足够的街道交叉口,留出100-200平米的开放空间,设置遮阳棚、公共座椅,允许开设小型咖啡馆、报亭或者小吃摊,空间不能完全封闭,要能让人一眼看到里面的活动。 > 约束条件:这个空间不能紧邻城市主干道,避免噪音干扰;周围500米内要有住宅区或者办公区,保证稳定的人流;不能设置太高的围墙,要保持空间的开放性。
你看,这个模式从来没有说“你必须在这里建一个星巴克”,它给的是解决问题的核心原则,具体的实现可以根据当地的实际情况调整。亚历山大在书里特别强调:“模式不是菜谱,不是让你照着做就能做出好菜;模式是语言,你掌握了这门语言,才能自己写出好的诗。”他反对的是把模式当成教条,反对的是设计师不用思考,直接套用模板。
《建筑模式语言》出版后,在建筑界引起了巨大的轰动,它第一次把建筑设计的“经验”变成了可复用、可传播的“知识”,很多建筑院校把它当成了必修教材,甚至影响了后来的城市规划和建筑设计。但谁也没想到,这套诞生于建筑领域的理论,会在十几年后彻底改变软件行业的开发方式。
---
二、伯克利的咖啡馆:四个工程师的灵光一闪
时间来到1990年,美国加利福尼亚州伯克利校园的Peet's咖啡馆里,四个来自不同国家的工程师正围在一张橡木桌旁,桌上堆着半凉的咖啡、写满C++代码的便签纸,还有一本翻得起毛的《建筑模式语言》。这是他们第三次因为同一个问题吵得面红耳赤:面向对象编程明明封装、继承、多态三大特性样样俱全,为什么写出来的代码还是乱成一团,扩展起来难如登天?
这四个人就是后来被软件行业称为“GoF(Gang of Four,四人组)”的Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides,他们当时都是面向对象编程领域的活跃研究者,各自都有过开发大型软件框架的惨痛经历。
- Erich Gamma是瑞士人,当时在瑞士联邦理工学院读博士,他的研究方向是面向对象的可复用性,过去三年他一直在开发一个叫ET++的跨平台C++ GUI框架,这个框架支持Windows、Mac、X Window三个系统,本来是想做一个可复用的绘图工具,结果每次要加新的图形类型(比如从圆形、方形加个三角形),或者要支持新的操作系统,他都要改至少三个核心类的代码,改一次要测试两周,bug还层出不穷。
- Richard Helm是澳大利亚人,当时在悉尼大学做研究员,他之前开发过一个图形编辑器,要支持用户给图形加各种样式(比如边框、阴影、渐变),每次加新的样式,他都要在所有的图形类里加一堆判断逻辑,代码很快就膨胀到了十几万行,没人敢动,加个小功能要改一周。
- Ralph Johnson是美国人,当时在伊利诺伊大学香槟分校的计算机系任教,他是面向对象编程的早期鼓吹者,但是他自己开发的文档编辑器框架,扩展性差到学生做个课程设计都要改核心代码,他每年都要花大量时间帮学生改bug,根本没时间做研究。
- John Vlissides是荷兰人,当时在斯坦福大学读博士,他的研究方向是面向对象的可视化,他开发的编译器框架,每次要加新的语法支持,都要改整个编译器的逻辑,重构了一次之后,新加的语法反而让原来的功能出了一堆bug。
四个人在1990年的OOPSLA(面向对象编程、系统、语言与应用大会)上相遇,聊起各自的开发困境,发现大家遇到的问题几乎一模一样:框架的扩展性极差,加新功能要改老代码,不同模块之间耦合得死死的,复用已有的代码比重新写一个还难。他们当时已经总结了十几个应对这些问题的经验,比如“把对象创建的逻辑和使用逻辑分开”“让一个对象的状态变化能自动通知所有依赖它的对象”,但是这些经验都是零散的,没有系统性的整理,也不知道怎么给别人讲清楚。
讨论到第三天,Richard Helm突然想起自己之前翻过的一本建筑学的书,就是亚历山大的《建筑模式语言》。他当时只是好奇建筑学怎么搞“模式”,随手翻了翻,没想到看到里面关于“独立构件”的描述时,突然有种醍醐灌顶的感觉:亚历山大说“要让建筑里的每个构件都能独立替换,不会因为一个构件坏了或者要升级,就要把整个建筑拆了”,这不就是他们一直在追求的软件框架的可扩展性吗?
他把这个想法说出来,其他三个人眼睛都亮了。他们当场凑钱买了一本《建筑模式语言》,回到酒店翻了三天,越翻越震惊:亚历山大的模式语言里的很多原则,居然完全适用于软件设计!比如“每个模式都要解决一个反复出现的问题”——软件设计里反复出现的问题不也是一样吗?比如“对象创建耦合”“状态变化通知难”“算法切换复杂”“接口不兼容”……这些问题他们每个人都踩过无数次坑,每个团队都在重复踩同样的坑。如果能把软件领域这些反复出现的问题和对应的解决方案也总结成“模式”,是不是就能让所有的工程师不用再重复踩坑?
这个想法一出来,四个人立刻开始了为期三年的“模式淘金”工作。他们把自己过去十年开发框架时踩过的所有坑都翻了出来,把每个问题的解决方案都整理成和亚历山大的模式一样的结构:问题背景、解决方案核心、代码示例、适用场景、优缺点。一开始他们总结出了几十个模式,后来反复筛选,去掉了那些适用场景太窄、或者已经被其他编程语言特性解决的模式,最终留下了23个最经典、最有代表性的模式,每个模式都对应了一个在面向对象开发中反复出现的核心问题。
写书的过程也不是一帆风顺的。当时很多人对他们的想法嗤之以鼻,觉得“软件设计是艺术,哪有什么固定的模式?”。1992年,他们第一次把模式草稿拿到OOPSLA大会上去宣讲,台下的反应很冷淡,有人甚至直接站起来说“你们这是给程序员搞的菜谱,会限制创造力”。四个人争论了很久,最后Ralph Johnson说了一句:“亚历山大说模式不是菜谱,是语言。你连话都不会说,怎么写出好诗?”这句话最后被他们写进了书的前言里。
1994年,这本被后来称为“软件行业圣经”的《设计模式:可复用面向对象软件的基础》正式出版[^2]。一开始他们只印了1000本,定价39.95美元,结果一年之内就卖光了,O'Reilly出版社赶紧加印,后来这本书被翻译成了20多种语言,销量超过100万册,成了计算机领域历史上最畅销的技术书籍之一。
---
三、被误解的三十年:设计模式不是银弹,也不是教条
《设计模式》出版后的三十年里,它彻底改变了软件行业的开发方式:无数工程师通过这本书学会了怎么写出可扩展、可维护的代码,无数的框架和库用到了里面的模式思想,甚至“设计模式”这个词本身,成了软件领域最流行的术语之一。但与此同时,这本书也被误解了三十年。
误解一:设计模式是银弹,用了就能写出好代码
这是最常见的误解。很多新手程序员刚学了23种模式,恨不得写个Hello World都要套个单例模式,写个加法方法都要套个策略模式,结果代码越写越复杂,本来一个方法能搞定的功能,非要加三四个类、七八个接口,可读性反而下降了。
这种误解的根源是搞错了模式的定位:模式是应对特定问题的经验沉淀,不是万能的解决方案。你写一个一次性运行的脚本,跑完就删,根本不需要考虑扩展性,套任何模式都是多余的;你做一个只有你一个人用的小工具,以后根本不会加新功能,也不用考虑什么开闭原则,能跑通就行。模式的价值是在代码需要长期维护、多人协作、频繁迭代的场景下才体现出来的:当你的项目有几十个开发人员一起写,要迭代好几年,要加几十上百个新功能的时候,之前总结好的模式能帮你少踩很多坑,少写很多垃圾代码。
误解二:不用设计模式就是代码写得烂
这个误解比上一个更离谱。很多“模式教条主义者”觉得,代码里没有工厂、没有观察者、没有代理,就是烂代码,就是新手写的。但事实上,好的代码第一标准是能正确、高效地解决问题,其次才是可维护、可扩展。很多优秀的开源项目,比如Linux内核,几乎没用到任何GoF总结的经典设计模式,大量用的是面向过程的设计,但是它的可读性、可维护性、性能都是工业级的,比很多硬套模式的Java代码强得多。
甚至很多GoF的成员自己都反对过度使用模式。Erich Gamma后来在一次采访里说:“我最讨厌看到有人为了用模式而用模式,明明一个简单的if-else就能搞定,非要套个策略模式,这是本末倒置。”[^3] 模式是工具,不是目的,你能用最简单的方案解决问题,比硬套十个模式都强。
误解三:设计模式是面向对象编程专属的
这个误解也很普遍,很多人觉得只有用Java、C++这种面向对象语言才能用设计模式,用Go、Python、JavaScript这种“非纯面向对象”语言就用不了。但实际上,模式是应对反复出现的问题的经验沉淀,和编程范式没有关系。GoF当年总结的是面向对象兴起时代的类级别模式,只是刚好赶上了面向对象的热潮,所以被大家熟知。
现在函数式编程里的管道模式、柯里化、Monad,微服务架构里的熔断、降级、服务网格,前端开发里的状态管理模式,都是不同领域下总结出来的“模式”。甚至你写SQL的时候,把重复的查询逻辑封装成视图,也是一种“模式”的体现——本质上都是把反复出现的问题的解决方案沉淀下来,供以后复用。
---
四、模式的本质:不是教条,是最优实践的语言
澄清了这些误解之后,我们才能理解设计模式的真正本质:它不是凭空创造的教条,也不是只有资深工程师才能用的高级技巧,它是无数工程师在踩坑中沉淀下来的最优实践语言。
这个本质可以从两个层面来理解: 第一个层面,模式是减少重复踩坑的经验包。你刚工作的时候写代码,可能把对象创建的逻辑写死在每个业务方法里,加个新的图形类型要改几十处代码,踩过一次坑之后,你下次再遇到类似的问题,就会知道要把创建逻辑抽出来,这就是工厂模式的雏形。只是之前没有人把这种经验总结出来,给你起个名字,告诉你“这个方案已经被无数人验证过,是可行的,你不用自己再踩一遍坑”。模式的价值就是把这些零散的经验系统化,让新手也能快速学到前人的经验,不用把所有的坑都踩一遍。
第二个层面,模式是工程师之间的交流 shorthand(速记符号)。你想想,如果你和同事协作开发,你要告诉他“我要让这个用户对象的状态变化的时候,自动通知所有的登录设备,让它们更新登录状态”,你要说一大堆话,他还不一定能完全理解。但如果你说“这里我用个观察者模式,你那边实现Observer接口就行”,他立刻就懂了,不用多解释。模式就像编程领域的专业术语,大大降低了工程师之间的沟通成本,提升了团队协作的效率。
> 📌 侧边栏(电子版交互):什么是“模式”的通用定义? > 不管建筑领域的模式、软件设计模式、还是现在各个领域总结的模式,核心定义都是相通的:每个模式都描述了一个在特定环境下反复出现的问题,然后给出了该问题的解决方案的核心,你可以重复使用这个解决方案,不用再重复做同样的工作。这个定义是亚历山大学建筑模式的时候提出的,GoF在写书的时候直接沿用了这个定义,现在也被所有的模式研究者认可。
---
五、模式的演进:从23种到无限可能
很多人对设计模式的另一个误解是:设计模式只有GoF总结的23种,背完这23种就天下无敌了。实际上,设计模式从来不是一成不变的,它是随着技术发展不断演进的。
GoF的23种模式是1994年总结的,针对的是90年代的面向对象开发场景,解决的是类级别的设计问题。后来随着软件行业的发展,又总结出了很多更高层级的模式:比如架构层面的分层模式、微服务模式、云
后来随着软件行业的发展,又总结出了很多更高层级的模式:比如架构层面的分层模式、微服务模式、云原生的熔断降级模式,再到前端开发的状态管理模式、数据工程的管道模式,甚至你现在用的手机APP里的手势交互逻辑、推荐算法的排序规则,本质上都是不同领域沉淀下来的“模式”——它们都遵循着亚历山大当年提出的“问题-解决方案-约束”的核心结构,只是解决的问题从建筑构件变成了软件模块、系统架构、甚至产品交互。
设计模式的演进从来不是孤立的,每一次技术浪潮的兴起,都会催生出对应领域的新模式,而所有模式的内核从来没有变过。我们可以通过下面这条时间线,清晰地看到模式思想从建筑学到软件工程,再到如今各个领域的完整迁徙路径:
```mermaid timeline title 设计模式演进时间线 1963年 : 亚历山大提出模式语言思想,开始调研建筑模式 1977年 : 《建筑模式语言》出版,总结253个建筑模式 1990年 : GoF四人组在OOPSLA大会相遇,萌生软件模式想法 1992年 : GoF首次在OOPSLA宣讲模式草稿,遭遇行业质疑 1994年 : 《设计模式:可复用面向对象软件的基础》出版,23种经典模式诞生 1996年 : Martin Fowler出版《企业应用架构模式》,扩展架构级模式覆盖范围 2000年 : J2EE生态流行,设计模式成为企业级开发标配,也出现大量滥用问题 2010年 : 微服务架构兴起,分布式系统、服务治理类模式成为主流 2015年 : 云原生、前端框架(React/Vue)兴起,领域特定新模式不断涌现 2023年 : AI辅助编程时代,模式与AI结合成为新的技术趋势 ```
这张时间线背后,藏着模式思想演进的三个核心规律:第一,模式永远是“问题驱动”的,哪个领域有反复出现的痛点,哪个领域就会总结出对应的模式;第二,模式永远在“分层”,从最初建筑学的物理空间层,到GoF的类/对象层,再到后来的架构层、系统层、交互层,覆盖的粒度越来越粗,解决的问题越来越复杂;第三,模式永远在“反对教条”,从亚历山大反对把模式当菜谱,到GoF反对为了用模式而用模式,再到今天行业反对模式教条主义,这个核心思想从来没有变过。
架构级模式的补全:从类设计到系统设计的延伸
GoF的23种模式诞生于面向对象编程的黄金时代,解决的都是类级别的设计问题:比如怎么创建对象、怎么处理对象之间的通信、怎么封装算法的变化。但到了90年代中后期,企业级应用开发越来越复杂,很多问题已经超出了类设计的范畴:比如怎么分层让前端和后端解耦?怎么访问数据库让业务代码不依赖具体的数据库实现?怎么管理用户会话让分布式系统能共享登录状态?这些问题用GoF的23种模式很难直接解决,于是1996年,Martin Fowler出版了《企业应用架构模式》,总结了16种架构级的模式,比如分层模式、数据映射器模式、单元OfWork模式、前端控制器模式,这些模式补全了GoF模式没有覆盖到的架构层问题,成了后来所有后端框架的设计基础。
比如你现在用的Spring框架,核心就是分层模式、依赖注入模式(本质是工厂模式的延伸)、AOP代理模式(就是GoF的代理模式)的组合,而MyBatis框架的核心就是数据映射器模式,把数据库的SQL查询和业务对象解耦,你不用改业务代码,只要改配置文件就能换数据库,这就是模式的价值。
再比如2010年微服务架构兴起之后,行业又总结出了一整套分布式系统模式:服务发现、熔断、降级、限流、API网关、事件驱动、CQRS(命令查询职责分离),这些模式和GoF的类级别模式不一样,解决的是分布式系统独有的问题:比如某个服务挂了怎么不影响整个系统?怎么保证分布式事务的一致性?怎么让多个服务之间不用直接调用也能通信?这些模式现在已经是所有互联网公司架构师必备的知识,甚至很多公司的新人培训都会把这些模式和GoF的23种模式一起讲。
你可以把GoF的23种模式比作“编程语法”,而架构级、系统级的模式比作“写文章的修辞手法”:语法是基础,但光有语法写不出好文章,还要会用修辞手法组织结构、表达思想。很多新手只会背23种模式,却不知道架构级模式的存在,写出来的代码类设计得很好,但整体架构乱成一团,扩展性还是很差,这就是只知其一不知其二。
被滥用的年代:设计模式的“原罪”
2000年到2010年,是设计模式被滥用的最严重的年代。当时Java语言如日中天,互联网行业刚开始兴起,大量