设计模式综合对比与选型:创建型、结构型、行为型模式的对比
说实话,设计模式学了那么多,真正到项目里该怎么选,反而是最让人头疼的事。我见过不少同事,学完23种经典模式后,恨不得每个类都套个工厂,每个方法都搞个观察者——结果代码反而变得又臭又长。
今天我就把这三类模式掰开揉碎了讲清楚。你想想看,设计模式说白了就是解决特定问题的套路。选对了,代码优雅得像首诗;选错了,那就是给自己挖坑。
三类模式的本质区别
我们先从最宏观的角度看。创建型模式关心的是「对象怎么生出来」,结构型模式关心的是「对象怎么组合在一起」,行为型模式关心的是「对象之间怎么通信」。嗯,这个划分其实很符合我们写代码的直觉。
| 类别 | 核心关注点 | 典型问题 | 我常用的模式 |
|---|---|---|---|
| 创建型 | 对象的创建与初始化 | new 关键字满天飞,耦合严重 | 工厂方法、建造者 |
| 结构型 | 类与对象的组合 | 继承层次过深,接口不匹配 | 适配器、组合模式 |
| 行为型 | 对象间的职责分配与通信 | 条件分支爆炸,逻辑分散 | 策略模式、状态模式 |
我的经验之谈:判断一个模式属于哪一类,就看它解决的是「创建」「结构」还是「行为」问题。如果一个问题同时涉及多个方面,那就需要组合使用模式了。
创建型模式:别让 new 毁了你的代码
我记得刚入行那会儿,写代码特别喜欢直接 new。后来维护一个老项目,发现一个类被 new 了上百次,每次参数还不一样。改一个构造函数的签名,改到想吐。
创建型模式的核心价值,就是把「怎么创建」和「谁来创建」解耦。我个人习惯这样选:
- 单例模式:全局只需要一个实例的时候用。比如配置管理器、连接池。但要注意,单例用多了会变成全局变量,测试起来很痛苦。
- 工厂方法:当创建对象的逻辑比较复杂,或者需要根据条件决定创建哪个子类时。我在做支付系统时,就用工厂方法根据支付类型创建不同的支付处理器。
- 建造者模式:对象构造参数太多,或者构造过程需要分步骤时。比如构建一个复杂的查询对象,用建造者模式比用一堆 setter 清晰得多。
避坑指南:我曾经在一个项目中,为了「优雅」把所有的 new 都换成了抽象工厂。结果代码量翻了三倍,可读性反而下降了。记住,模式是工具,不是目的。如果只是简单创建,直接 new 反而更好。
结构型模式:组合的艺术
结构型模式解决的是「怎么搭积木」的问题。你想想看,当你的类继承层次超过三层,或者一个类需要同时扮演多个角色时,就该考虑结构型模式了。
我遇到过最典型的场景:一个老系统里有个第三方库的接口,跟我们的新系统完全不兼容。改第三方库不现实,改自己的系统又太伤筋动骨。这时候适配器模式就派上用场了——写个包装类,把不兼容的接口转成我们需要的。
结构型模式的选型要点:
- 适配器模式:接口不匹配,但双方都不方便改。说白了就是「翻译官」。
- 装饰器模式:需要动态给对象添加功能,而且不想用继承。比如给数据流加缓冲、加密、压缩等功能。
- 组合模式:树形结构,部分和整体需要统一对待。文件系统、组织架构图都是典型例子。
- 代理模式:需要控制对对象的访问。延迟加载、权限控制、远程调用都用得上。
注意:结构型模式容易过度设计。我见过有人为了「可扩展」,给一个只有两个方法的接口加了代理、装饰器、适配器三层包装。结果后来需求变了,改一个功能要动四个文件。嗯,这就是典型的「为了用模式而用模式」。
行为型模式:让对象好好说话
行为型模式是我觉得最有趣的一类。它解决的是对象之间「怎么协作」的问题。说白了,就是让对象们好好说话,别吵架。
我做过一个工作流引擎,里面各种状态转换、条件判断、事件通知。刚开始用 if-else 写,写到后面自己都看不懂了。后来用状态模式重构,每个状态一个类,转换逻辑清晰多了。
行为型模式的选型建议:
- 策略模式:算法或行为可以互相替换。比如排序算法、促销策略、验证规则。这是我最常用的行为型模式。
- 观察者模式:一对多的依赖关系,一个对象变化需要通知多个对象。事件驱动系统、消息队列都是这个思路。
- 状态模式:对象的行为依赖于它的内部状态。跟策略模式很像,但状态模式的状态是动态变化的。
- 命令模式:把请求封装成对象,支持撤销、重做、队列等操作。编辑器里的撤销功能就是典型应用。
核心判断标准:如果一段代码里充满了 if-else 或 switch-case,而且每个分支做的事情差异很大,那大概率可以用策略模式或状态模式来重构。我曾经把一个 200 行的 switch 语句重构成了 8 个策略类,每个类 20 行,维护起来舒服多了。
如何根据业务场景选型
好了,三类模式都讲完了。那具体到项目里,到底怎么选?我总结了一个简单的决策流程:
- 先看问题本质:是创建对象的问题?还是组合结构的问题?还是行为协作的问题?
- 再看变化点:未来哪些地方可能会变?把变化封装起来,就是模式要解决的问题。
- 最后看复杂度:引入模式带来的好处是否大于增加的复杂度?如果只是简单场景,别硬上模式。
举个例子。假设你要做一个报表生成系统,支持 PDF、Excel、HTML 三种格式。你会怎么设计?
- 创建报表对象 -> 可以用工厂方法或简单工厂
- 报表的样式装饰 -> 可以用装饰器模式
- 报表生成策略 -> 可以用策略模式
你看,一个业务场景可能同时用到多种模式。但关键是,不要一开始就想着「我要用哪些模式」,而是先想清楚「我要解决什么问题」,模式自然就浮现出来了。
我的习惯:写代码前先画个简单的类图,把核心类和它们的关系画出来。如果发现某个关系特别复杂,或者某个类职责太多,那就是需要引入设计模式的信号了。
三类模式的关系图谱
下面这张图是我自己总结的,展示了三类模式在项目中的典型协作关系。你可以看到,一个完整的系统往往需要多种模式配合使用。
从这张图你可以看到,三类模式并不是孤立的。创建型模式负责把对象生出来,结构型模式负责把它们组合好,行为型模式负责让它们协作起来。而且,行为的结果往往会反馈到创建策略上——这就是一个完整的闭环。
总结
设计模式选型没有银弹。我见过用单例模式用得飞起的项目,也见过完全不用模式但代码依然清晰的项目。关键不在于用了多少模式,而在于是否解决了实际问题。
最后分享一个我自己的原则:先写出能跑的代码,再考虑要不要用模式重构。不要一开始就想着套模式,那样很容易过度设计。等代码写出来了,你自然能看出哪些地方需要模式来优化。
一句话记住:创建型管生,结构型管养,行为型管协作。选型时先看问题本质,再看变化点,最后评估复杂度。别为了用模式而用模式,那是在给自己挖坑。