一、设计模式概述:设计模式的前世今生、GoF 23种设计模式分类、设计模式在大型项目中的价值
1.1 设计模式的前世今生
说起设计模式,得先聊聊它的起源。这个概念最早不是程序员发明的,而是建筑领域的。
1977年,一位叫克里斯托弗·亚历山大的建筑学家写了本书,叫《建筑模式语言》。他提出一个观点:好的建筑都有一些可复用的结构规律。比如窗户怎么开、门朝哪个方向、房间怎么布局——这些规律总结出来,就是「模式」。
嗯,这跟我们写代码有什么关系?
关系大了。1994年,四位软件工程师——Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides——把这套思想搬到了软件开发中。他们合著了一本书,叫《设计模式:可复用面向对象软件的基础》。这四个人后来被称为「GoF四人组」(Gang of Four)。
这本书里,他们总结了23种设计模式。说白了,就是23种经过验证的、解决常见软件设计问题的「套路」。
我记得刚入行那会儿,第一次读到这本书,说实话——没看懂。觉得太抽象了,什么「工厂」「代理」「观察者」,感觉跟写业务代码八竿子打不着。后来做了三五年项目,踩了不少坑,再回头翻这本书,才恍然大悟:原来我踩的坑,人家早就总结好了解决方案。
1.2 GoF 23种设计模式分类
GoF把23种模式分成了三大类:创建型、结构型、行为型。这个分类是有讲究的——它对应着你在设计软件时,要解决的三个核心问题。
| 分类 | 核心问题 | 包含模式 |
|---|---|---|
| 创建型 | 对象怎么创建?谁来创建? | 单例、工厂方法、抽象工厂、建造者、原型 |
| 结构型 | 类和对象怎么组合成更大的结构? | 适配器、桥接、组合、装饰、外观、享元、代理 |
| 行为型 | 对象之间怎么交互、怎么分配职责? | 模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器 |
你想想看,这23种模式,其实覆盖了你在写代码时最常遇到的场景。比如:
- 创建型:你不想每次new对象都写一堆初始化代码?用工厂模式。
- 结构型:你想让两个不兼容的接口一起工作?用适配器模式。
- 行为型:你希望一个对象状态变化时,自动通知其他对象?用观察者模式。
我在项目中遇到过最典型的例子:一个支付系统,对接了微信、支付宝、银联三种渠道。每个渠道的接口参数、签名方式、回调逻辑都不一样。如果硬编码,代码会乱成一锅粥。后来用了策略模式+工厂模式,每种支付方式封装成一个策略类,工厂根据参数动态创建。嗯,代码瞬间清爽了。
1.3 设计模式在大型项目中的价值
为什么大型项目特别需要设计模式?我总结了几点:
1.3.1 应对变化
大型项目最大的特点是什么?需求一直在变。今天加个功能,明天改个逻辑,后天换个第三方服务。如果没有好的设计,每次变更都得改一大片代码。
设计模式的核心价值之一,就是「封装变化」。比如策略模式,把算法封装成独立的策略类,新增一种策略,不需要改原有代码。这就是开闭原则——对扩展开放,对修改关闭。
我曾经在一个电商项目中,促销规则从最初的「满减」扩展到「折扣」「秒杀」「拼团」「返现」……如果一开始没用策略模式,后面每加一种规则,都得改核心逻辑,那简直是灾难。
1.3.2 提升可读性
你想想看,一个大型项目,少说几十万行代码,多则上千万行。新同事接手,光看懂代码就得花几个月。
设计模式提供了一套「通用词汇」。你说「这里用了观察者模式」,有经验的程序员立刻就知道:哦,这里是一对多的依赖关系,一个对象变化,其他对象自动更新。不需要再一行一行读代码去猜。
说白了,设计模式就是程序员之间的「行话」。用好了,沟通效率翻倍。
1.3.3 降低耦合
耦合是大型项目的头号敌人。模块之间耦合越紧,牵一发而动全身,改一个地方,崩一片功能。
设计模式很多都在解决耦合问题。比如中介者模式,把多个对象之间的网状交互,变成通过中介者统一调度。再比如外观模式,给复杂子系统提供一个简单的统一接口。
1.3.4 促进复用
设计模式本身就是可复用的解决方案。你不需要每次都从零开始设计架构,直接套用成熟的模式,既省时间又少踩坑。
比如单例模式,全局只需要一个实例的场景(配置管理器、线程池、数据库连接池),直接用单例,省得重复创建浪费资源。
再比如模板方法模式,把算法的骨架固定下来,具体步骤留给子类实现。这样,相同的流程不用重复写,不同的细节各自实现。
1.4 小结
这一章我们聊了设计模式的起源、分类,以及它在大型项目中的价值。说白了,设计模式就是前辈们踩过无数坑之后,总结出来的「最佳实践」。
我个人习惯是:学设计模式,不要死记硬背。先理解它解决的是什么问题,然后在项目中遇到类似问题时,试着用一用。用着用着,你就发现——原来这些模式,早就藏在你的代码里了。
下一章,我们会深入第一个模式——单例模式。这是最常用、也最容易用错的一个模式。到时候我会分享一些我在项目中踩过的坑,以及正确的使用姿势。