走进设计模式的神奇世界
第 1 章:走进设计模式的神奇世界
1.1 积木世界里的智慧
想象一下,你面前有一堆积木,有红色的、蓝色的、绿色的,有方形的、圆形的、三角形的。你用这些积木搭过什么呢?是一座城堡?一辆汽车?还是一只恐龙?不管你搭过什么,有没有发现这样一个有趣的现象:当你第一次搭城堡的时候,可能会觉得很难,不知道从哪儿开始,搭出来的样子也可能不太好看。但是当你搭过几次之后,你会发现:“哦,原来要先放一块大的积木做地基,然后一层一层往上叠,这样城堡才会稳固!”这时候,你就掌握了一个搭建城堡的诀窍。
这个“搭建城堡的诀窍”是什么呢?它不是某一块积木的名字,也不是某一种颜色的积木。它是一种方法,一种思路,一种告诉你“先做什么、再做什么”的智慧。你学会这个方法之后,每次搭建城堡都会又快又好,甚至还能教给别的小朋友。
现在让我们再想一个问题:你用积木搭汽车的时候,是不是也有一套方法?比如,先搭车身,再装轮子,最后加上车灯。这和搭建城堡的方法完全不同。为什么呢?因为汽车和城堡是两种不同的东西,它们需要不同的搭建方法。这就告诉我们:不同的问题,需要不同的解决方法。
在日常生活中,像这样被人们反复使用、经过验证、行之有效的解决方法,我们就叫它们模式。模式就像是一本“聪明办法手册”,里面记录着各种问题的最佳答案。当你遇到类似的问题时,只要翻开手册,找到对应的办法,就能轻松解决问题。
1.2 从积木到代码:模式无处不在
你可能会问:“这和电脑程序有什么关系呢?”关系可大了!让我们一起来看看。
电脑程序是什么呢?简单来说,程序就是一系列告诉电脑“做什么、怎么做”的指令。就像你用积木搭一个作品需要“搭建方法”一样,编写一个电脑程序也需要“编写方法”。
当我们用编程语言编写程序时,我们会创建很多类。什么是类呢?你可以把它想象成一块特殊的积木。这块积木很神奇,它可以自己决定里面装什么(我们叫它属性,就像积木的颜色和形状),也可以自己决定能做什么(我们叫它方法,就像积木能不能发光、能不能发出声音)。
举一个具体的例子吧。假设我们要编写一个“学生信息管理系统”,用来记录每个同学的名字、年龄、班级等信息。在这个程序里,我们会创建一个叫做“学生”的类。这个类会有一些属性:姓名(张三、李四)、年龄(8岁、9岁)、班级(一年级一班、二年级二班)……这个类也会有一些方法:比如“计算年龄”(从出生日期算到现在)、“更换班级”(当你升年级时)等。
现在问题来了:当我们需要编写“学生信息管理系统”的时候,应该怎么组织这些类呢?可以有很多种方法:我们可以把所有功能都塞进一个巨大的类里,就像用一块超级大的积木搭整个作品;我们也可以把功能分散到很多很多小类里,每个类只做一点点事情,就像用很多很多小小的积木搭作品。
哪种方法更好呢?这个问题,就是软件设计要解决的问题。
就像搭积木有“搭建方法”一样,编写程序也有“编写方法”。有些编写方法能让程序清晰易懂、易于维护,就像用正确的方法搭积木,作品既漂亮又稳固;有些编写方法则会让程序混乱不堪、漏洞百出,就像用错误的方法搭积木,作品很容易倒塌。
经过几十年的探索和实践,全世界的程序员们发现:有些程序设计方法是如此优秀、如此有效,以至于被反复使用、不断验证,最终成为了公认的“最佳答案”。这些经过验证的最佳程序设计方法,就是我们要学习的设计模式。
1.3 图书馆里的启示
为了更好地理解为什么程序员需要设计模式,让我们来玩一个思维游戏。
假设你家里有一本书,叫《哈利·波特》。如果你只有一本书,随便放在哪里都能找到。但是如果你的书架上有100本书呢?你可能就需要一个整理方法了——按照书名首字母排列?按照书的类型排列?还是按照购买日期排列?
现在想象一个更大的图书馆,里面有100万本书!如果没有一套科学的分类方法,管理员们肯定要疯了:想要找一本《哈利·波特》,得把整个图书馆翻个底朝天。但是有了科学的分类方法就完全不一样了。管理员们会把书分成不同的类别:小说、科普、历史、童话……在小说下面再细分:奇幻小说、侦探小说、科幻小说……在奇幻小说下面再细分……这样一来,不管你想找什么书,只要按照分类一层一层往下找,很快就能找到。
这个故事告诉我们一个重要的道理:当东西少的时候,随便放就行;当东西多的时候,就必须有组织、有分类、有方法。
程序员写的程序也是一样的道理。早期的程序很简单,可能只有几百行代码,随便怎么写都能跑。但是现在的程序越来越复杂了。一个手机App可能有几十万行代码,一个操作系统可能有几千万行代码。如果程序员写代码的时候没有章法,想到什么写什么,程序很快就会变成一团乱麻——自己写的代码,过两个月就不知道是什么意思了;同事写的代码,根本看不懂;想加一个新功能,不知道该加在哪里;程序出了bug,根本找不到问题出在哪里……
这时候,设计模式就像图书馆的分类系统一样,给程序员们提供了一套科学的“组织方法”。有了设计模式,程序员们就知道:遇到这种问题,应该用这种方式组织代码;遇到那种问题,应该用那种方式组织代码。这样写出来的程序,结构清晰、逻辑分明、容易理解、便于维护。
1.4 代码城市的比喻
在正式开始学习设计模式之前,让我们先来建立一个有趣的比喻体系。这个比喻体系会贯穿整本书,帮助你更直观、更深刻地理解设计模式。
程序是一座城市
我们把程序比作一座城市。就像城市里有各种各样的建筑、道路、公共设施一样,程序里也有各种各样的代码模块、功能组件、数据结构。城市的规模大小不一,有小镇、有县城、有大都市;程序的规模也大小不一,有小程序、有中大型系统、有超级平台软件。
城市需要规划和管理才能正常运行,需要水、电、交通、通讯等基础设施;程序也需要架构和设计才能稳定运行,需要数据存储、网络通信、用户界面等技术支撑。一个规划混乱的城市会交通堵塞、设施重复、资源浪费;一个设计糟糕的程序会运行缓慢、漏洞百出、维护困难。
类是一栋建筑
我们把类比作城市里的建筑。就像每栋建筑都有特定的功能(住宅楼让人居住、写字楼让人办公、商场让人购物),每个类也有特定的功能(学生类管理学生信息、汽车类控制汽车行为、游戏类运行游戏逻辑)。
建筑有外观和内部结构,外观决定了它看起来怎么样,内部结构决定了它是怎么建成的、牢不牢固;类也有接口和实现,接口定义了它能做什么,实现决定了它具体是怎么做的。一栋设计良好的建筑,不仅外观漂亮,而且内部结构合理,住起来舒适安全;一个设计良好的类,不仅接口清晰,而且实现高效,运行起来稳定可靠。
建筑和建筑之间不是孤立的,它们通过门、窗、走廊、道路相互连接,形成一个有机的整体;类和类之间也不是孤立的,它们通过继承、组合、依赖等关系相互协作,实现复杂的功能。
设计模式是一张蓝图
我们把设计模式比作建筑的蓝图。
什么是蓝图呢?在建筑工程中,建筑师在正式建造之前,会先画一张详细的图纸。这张图纸告诉工人:这栋楼应该建成什么样子,哪里是承重墙、哪里是隔断墙,哪里装门、哪里开窗,用什么材料、什么工艺……有了蓝图,工人就知道该怎么建造;没有蓝图,工人们就会手足无措,或者各行其是,最后建出来的建筑可能乱七八糟。
设计模式就像是程序员世界的“建筑蓝图”。它不是具体的代码,而是一种解决问题的思想和方法。它告诉程序员:遇到这类问题,应该这样组织你的代码;这里需要创建一个新对象,那里需要让几个类协同工作……有了设计模式,程序员就知道该怎么“建造”自己的程序;没有设计模式,程序可能会结构混乱、难以维护。
需要注意的是,蓝图是通用的、可复用的。一栋图书馆的蓝图可以用来建很多图书馆,一栋学校的蓝图可以用来建很多学校。同样地,一个设计模式可以用来解决很多类似的问题。比如“工厂模式”可以用来创建各种对象,“观察者模式”可以用来实现各种通知功能……掌握的设计模式越多,程序员解决问题的能力就越强。
1.5 认识23位“代码建筑师”
前面我们说过,设计模式是经过验证的最佳程序设计方法。那么,这些方法是谁发现的呢?又是怎么被整理出来的呢?
这就要说到一本著名的书了。1994年,四位著名的软件工程师——Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides——合作出版了一本书,书名叫《设计模式:可复用面向对象软件的基础》。这本书后来被人们亲切地称为“四人帮(Gang of Four,简称GoF)”的书,是软件设计领域的里程碑之作。
在这本书里,四位作者总结了23种最常用、最有效的设计模式。这些模式是他们从大量的软件项目中提炼出来的,代表了面向对象软件设计的最佳实践。直到今天,这23种设计模式仍然是软件工程师必学的经典内容。
模式的三大阵营
这23种设计模式根据其目的可以分为三大类:
第一类是创建型模式(Creational Patterns)。这类模式关注的是“如何创建对象”。在程序中,对象是最基本的组成单元。创建一个对象看起来很简单,但其实有很多讲究:什么时候创建?在哪里创建?谁来创建?创建过程复杂怎么办?创建多个相似的对象怎么办?创建型模式就是为了解决这些问题而设计的。就像建筑工人需要决定在哪里挖地基、用什么材料建地基一样,程序员也需要决定用什么方式创建对象。创建型模式包括5种:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
第二类是结构型模式(Structural Patterns)。这类模式关注的是“如何组合类或对象”。一个复杂的程序就像一座复杂的建筑,需要把很多“小积木”组合成“大建筑”。怎么组合?组合的原则是什么?组合之后怎么保持灵活性和可扩展性?结构型模式就是为了解决这些问题而设计的。就像建筑师需要决定承重墙怎么设置、房间怎么划分一样,程序员也需要决定类和对象怎么组织、怎么连接。结构型模式包括7种:适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式、代理模式。
第三类是行为型模式(Behavioral Patterns)。这类模式关注的是“如何分配职责和交互”。程序运行时,对象们不是静止不动的,它们需要相互通信、协作配合。谁通知谁?谁负责什么?怎么让对象们和谐共处?行为型模式就是为了解决这些问题而设计的。就像城市里的交通规则决定了车辆和行人怎么有序通行一样,行为型模式决定了对象之间怎么有序交互。行为型模式包括11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
下面这张表格可以帮助你更清晰地了解这23种设计模式的分类:
| 类别 | 数量 | 描述 | 代表模式 | |------|------|------|----------| | 创建型模式 | 5种 | 关注对象的创建机制 | 单例模式、工厂方法模式 | | 结构型模式 | 7种 | 关注类与对象的组合方式 | 适配器模式、装饰器模式 | | 行为型模式 | 11种 | 关注对象间的职责与交互 | 观察者模式、策略模式 |
1.6 为什么要学习设计模式
现在你可能会问:“我只是一个普通的小朋友,为什么要学习这些看起来很高深的设计模式呢?”
这个问题问得很好!让我们一起来看看学习设计模式能给你带来什么。
首先,学习设计模式可以让你理解软件的“内部秘密”。 你用过很多软件和游戏吧?微信、抖音、王者荣耀、我的世界……你有没有想过,这些软件是怎么设计出来的?为什么它们能同时运行很多功能?为什么它们能不断更新升级?当你学习了设计模式之后,你就会发现:“哦,原来微信的消息通知用的是观察者模式!”、“原来游戏里的装备系统用的是装饰器模式!”这时候,你看软件的角度就完全不同了,你会看到软件背后的“设计智慧”。
其次,学习设计模式可以培养你的逻辑思维能力。 设计模式不仅仅是一些编程技巧,更是一种思考问题、解决问题的方法论。当你学习设计模式的时候,你需要理解问题的本质、分析各种解决方案的优缺点、选择最适合的方案……这些训练会让你的思维变得更加严谨、更加系统、更加灵活。这种能力不仅对编程有用,对学习数学、科学甚至日常生活都有帮助。
第三,学习设计模式可以为你打开通往编程世界的大门。 如果你将来想成为一名软件工程师,那么设计模式是必修课。几乎所有的软件公司在招聘程序员时都会考察设计模式的知识。提前学习设计模式,就像是提前拿到了通往编程世界的“通行证”。
最后,学习设计模式可以让你体验思考的乐趣。 设计模式就像是一道道精妙的谜题。当你绞尽脑汁终于理解了一个模式的精髓时,那种豁然开朗的感觉是非常美妙的。而且,设计模式不是孤立存在的,它们之间有着千丝万缕的联系。当你发现这种联系的时候,你会感受到知识融会贯通的快乐。
1.7 本书的地图:23个模式的探索之旅
在接下来的章节里,我们将逐一探索这23种设计模式。每一种模式都会从以下几个方面来讲解:
模式的名称和别名——每种模式都有一个朗朗上口的名字,比如“工厂方法”、“观察者”、“策略”等。我们会解释这个名字的含义,以及它为什么叫这个名字。
模式的用途——这种模式是用来解决什么问题的?什么情况下应该使用它?我们会用大量的生活例子和编程场景来说明,让你真正理解模式的适用场景。
模式的结构——这种模式由哪些角色组成?它们之间的关系是什么?我们会画出详细的类图和时序图,让你一眼就能看清模式的“骨架”。
模式的实现——这种模式在代码中是怎么体现的?我们会提供多种编程语言的示例代码(主要是Python、Java和JavaScript,因为它们是最常用的编程语言),让你看到模式的具体实现方式。
模式的效果——使用这种模式有什么优点?又有什么缺点?需要注意什么问题?我们会进行深入的分析,帮助你全面评估是否要使用这种模式。
模式的对比——这种模式和类似的其他模式有什么区别?它们各自适用什么场景?我们会进行横向对比,让你对相关模式有更清晰的认识。
真实世界的应用——这种模式在真实的软件项目中是怎么使用的?我们会列举一些著名的开源库和框架,让你看到模式在实践中的应用。
动手练习——学习编程最好的方法就是动手实践。我们会提供一些练习题和小项目,让你通过实践加深对模式的理解。
下面,让我们先预览一下这23种设计模式的全貌,为接下来的学习之旅做好规划:
创建型模式(5种)
第2章:孤独的冠军——单例模式
你有没有想过,为什么Windows系统只能有一个“回收站”?为什么一个游戏只能有一个“记分器”?有些东西就是应该独一无二的,不能有多个副本。单例模式就是用来保证一个类只能创建一个实例的设计模式。我们会看看单例模式是怎么工作的,以及它在操作系统、数据库连接池、日志系统等场景中的应用。
第3章:工厂的智慧——工厂方法模式
工厂是做什么的?工厂是根据图纸生产产品的。工厂方法模式就是让一个类延迟决定要创建哪个具体类的实例。就像一个汽车工厂,它只知道要生产“汽车”,但具体是生产卡车还是轿车,要根据客户的要求来决定。我们会看看工厂方法模式是怎么实现这种“延迟创建”的。
第4章:工厂的家族——抽象工厂模式
如果说工厂方法模式是生产一种产品的工厂,那么抽象工厂模式就是生产一整套产品的工厂。就像一个家具厂不仅可以生产沙发,还可以生产配套的茶几、电视柜一样,抽象工厂模式可以创建一系列相关对象。我们会看看这种模式是怎么组织“产品族”的。
第5章:拼装的艺术——建造者模式
你有没有用乐高积木搭过复杂的模型?乐高的特点是先把零件一个个做好,然后按照说明书一步步组装。建造者模式就是这种“先分开做,再组合”的思想。我们会看看建造者模式是怎么把一个复杂对象的构建过程和表示分离的。
第6章:克隆的力量——原型模式
你玩过“复制”这个技能吗?在游戏里,有些角色可以变出和自己一模一样的分身。原型模式就是程序世界里的“复制术”。我们不需要从头创建对象,而是复制一个现有对象的模板。我们会看看原型模式是怎么实现对象克隆的,以及浅拷贝和深拷贝的区别。
结构型模式(7种)
第7章:万能的转换头——适配器模式
你去国外旅行的时候,需要带转换插头吧?因为不同国家的插座形状不一样。适配器模式就是程序世界里的“转换插头”。它可以把一个类的接口转换成另一个接口,让原本不兼容的类可以一起工作。我们会看看适配器模式是怎么充当“中间人”的。
第8章:桥梁的奥秘——桥接模式
桥梁是怎么建造的?先把桥墩建好,再把桥面架上去。桥墩和桥面是可以独立变化的——你可以用不同的材料建桥墩,也可以建不同的桥面。桥接模式就是这种“分离抽象和实现”的思想。我们会看看桥接模式是怎么避免类数量爆炸的。
第9章:装饰的艺术——装饰器模式
你给手机装过漂亮的手机壳吗?手机壳可以给手机增加新功能(比如防摔),同时又不改变手机本身。装饰器模式就是这种“在不改变原对象的情况下,给它添加新功能”的思想。我们会看看装饰器模式是怎么实现动态扩展的。
第10章:整体与部分——组合模式
一片森林是由一棵棵树木组成的。一棵树又由根、茎、叶组成。如果我们把森林、树、枝、叶都抽象成“整体-部分”的关系,就可以用统一的方式处理它们。组合模式就是这种“把对象组织成树形结构”的思想。我们会看看组合模式在文件系统、UI界面等场景中的应用。
第11章:简洁的外观——外观模式
你去图书馆借书,只需要在一个服务台办理,不用知道书是放在哪个仓库、是怎么分类的。外观模式就是提供一种简单的接口,隐藏复杂的内部实现。我们会看看外观模式是怎么简化系统和用户交互的。
第12章:共享的艺术——享元模式
在游戏里,可能有成千上万的士兵在战斗。如果每个士兵都占用大量内存,游戏就会变得很卡。享元模式就是“共享”的思想——把很多对象的共同部分提取出来共享,只保存不同的部分。我们会看看享元模式是怎么节省内存的。
第13章:代理的魔法——代理模式
你有没有找别人帮你排队买票的经历?代理模式就是程序世界里的“代人办事”。它为另一个对象提供一个替身,以控制对这个对象的访问。我们会看看代理模式在远程代理、虚代理、保护代理等场景中的应用。
行为型模式(11种)
第14章:策略的智慧——策略模式
你去北京旅游,可以坐飞机、坐火车、坐大巴。同样是“出行”这个行为,有不同的策略可以选择。策略模式就是定义一系列算法,把它们一个个封装起来,并使它们可以相互替换。我们会看看策略模式是怎么把算法从业务逻辑中分离出来的。
第15章:模板的传承——模板方法模式
你有没有看过烘焙食谱?食谱会告诉你:“步骤一:混合面粉和糖;步骤二:加入鸡蛋和牛奶;步骤三:放入烤箱烤20分钟。”至于具体烤什么点心,就看你自己决定了。模板方法模式就是