设计模式概述
写在课程前面的话
进入大三下学期,各位同学已经涉猎了整个软件学科几乎所有的内容,包括但不限于:编程语言、数据结构与算法、操作系统、数据库系统、计算机网络等。可以说,大家已经掌握了到社会上一搏的大部分技能,一只脚已经迈进了社会。然而,为什么本学期还要开设《软件设计与体系结构课程》?很多同学对此感到疑惑,因为在《软件工程》课程中,已经有一个章节来介绍相关内容。以下从个人、公司和软件行业发展的三个角度,详细介绍该课程的必要性和作用。
个人成长层面:本课程的核心目标在于推动同学们实现从“能写代码”到“能写出高质量代码”的蜕变。
首先,本课程内容不仅教授如何编写功能正确的代码,更重要的是教会同学们如何编写高质量、可维护、可扩展的代码。这包括代码的结构化、模块化和规范化。其次,本课程内容将帮助同学从单纯的编码者转变为具有设计思维的软件开发者。通过学习设计模式和架构原则,同学们能够更好地理解系统的整体结构和各个组件之间的关系。掌握软件设计与体系结构的知识,有助于同学们在未来的职业发展中更快地晋升到高级开发岗位,甚至成为软件架构师或项目经理。
公司运营层面:对于企业而言,一个清晰、灵活且易于维护的软件架构是确保项目长期成功的关键。采用设计模式和体系结构规范的代码,可以显著提高代码的可读性和可维护性。这对于大型项目的团队协作尤为重要,可以减少代码冲突和重复工作。模块化的设计方法使得代码更容易管理和扩展。当业务需求发生变化时,可以快速地添加或修改模块,而不会影响到其他部分的代码。良好的设计和架构可以促进团队成员之间的有效沟通和协作。每个人都能清晰地了解系统的整体结构和自己的职责范围,从而提高团队的工作效率。合理的软件设计和架构可以降低项目的风险,避免因设计缺陷导致的返工和延期。高质量的软件产品可以提高客户的满意度,增强公司的市场竞争力。
软件行业发展层面:从更宏观的角度看,软件设计模式和体系结构的普及,是推动软件工业向更高层次标准化、工业化生产方式迈进的重要力量。随着云计算、大数据、人工智能等新兴技术的兴起,软件系统的复杂度日益增加,传统的开发方式已难以满足高效、高质量的需求。软件设计模式和体系结构的推广,使得软件开发逐渐向工业标准化生产方式靠拢。这有助于提高软件开发的效率和质量,降低开发成本。因此,掌握先进的设计理念和架构方法,不仅能够提高软件开发团队的生产效率,还能有效保障软件产品的稳定性和安全性,促进整个软件行业的健康发展。
1.1 软件设计模式的产生背景
"设计模式"最初并不是出现在软件设计中,而是被用于建筑领域的设计中。在20世纪初,建筑大师们如勒·柯布西耶和弗兰克·劳埃德·赖特等,在他们的作品中反复运用了一些成功的解决方案,这些方案不仅体现了他们的创新思维,也反映了对于建筑功能和美学的深入理解。随着时间的推移,这些解决方案被逐渐总结和提炼,形成了所谓的“设计模式”。
1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫·亚历山大(Christopher Alexander)在他的著作《建筑模式语言:城镇、建筑、构造》(A Pattern Language: Towns Building Construction)中描述了一些常见的建筑设计问题,并提出了 253 种关于对城镇、邻里、住宅、花园和房间等进行设计的基本模式。这些模式在建筑领域内被广泛传播和应用,为后来的建筑设计提供了宝贵的经验和指导。
1979 年他的另一部经典著作《建筑的永恒之道》(The Timeless Way of Building)进一步强化了设计模式的思想,为后来的建筑设计指明了方向。
随着计算机科学的发展,软件设计领域的专家们借鉴了建筑学中的这一概念,将其引入到软件开发中,用以指导程序员解决复杂的设计问题,从而极大地推动了软件工程学科的进步。
1987 年,肯特·贝克(Kent Beck)和沃德·坎宁安(Ward Cunningham)首先将克里斯托夫·亚历山大的模式思想应用在 Smalltalk 中的图形用户接口的生成中,但没有引起软件界的关注。
1990年软件工程界开始研讨设计模式的话题,后来召开了多次关于设计模式的研讨会。直到1995 年,艾瑞克·伽马(ErichGamma)、理査德·海尔姆(Richard Helm)、拉尔夫·约翰森(Ralph Johnson)、约翰·威利斯迪斯(John Vlissides)等 4 位作者合作出版了《设计模式:可复用面向对象软件的基础》一书,在此书中收录了 23 个设计模式,这是设计模式领域里程碑的事件,导致了软件设计模式的突破。这 4 位作者在软件开发领域里也以他们的“四人组”(Gang of Four,GoF)著称。
1.2 软件设计模式的概念
软件设计模式(Software Design Pattern),又称设计模式,“解决如下的在特定的环境中反复出现的设计问题,并且为其提供了解决方案”(Buschmann, et. al. 1996),是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。
软件开发领域普遍认同这样一句格言:”不要重复造轮子“。从某种意义上讲,程序中如果出现大量重复的代码,则意味着这是一个缺乏设计的软件项目。面向对象编程语言的初学者写代码时往往是“东一榔头、西一棒槌”,想到哪里写到哪里,缺乏软件架构的全局观,最终造成系统中充斥大量的冗余代码,缺乏模块化的设计,更谈不上代码的复用性。代码量大并不能代表系统功能多么完备,更不能代表程序员多么努力与优秀,反之,作为有思想高度的开发者一定要培养“偷懒”意识,想方设法以最少的代码量实现最强的功能,这样才是优秀的设计。
设计模式主要研究的是“变”与“不变”,以及如何将它们分离、解耦、组装,将其中“不变”的部分沉淀下来,避免“重复造轮子”,而对于“变”的部分则可以用抽象化、多态化等方式,增强软件的兼容性、可扩展性。如果把编写代码比作建筑施工,那么设计模式就如同建筑设计图。它类似于乐高积木的设计理念,通过标准化的接口实现组件的自由组合,从而创造出新的结构。
经验丰富的开发人员都知道,随着需求的增加与变动,软件项目版本不断升级,维护也变得越来越难,修改或添加一个很简单的功能往往要耗费大量的时间与精力,牵一发而动全身,严重时甚至会造成整个系统的崩溃。优秀的系统不单单在于其功能有多么强大,更应该将各个模块划分清楚,并且拥有一套完备的框架,像开放式平台一样兼容对各种插件的扩展,让功能变动或新增变得异常简单,一劳永逸,这离不开对各种设计模式的合理运用。
设计模式并不局限于某种特定的编程语言,它是一种宏观的思维方式,基于前人经验的总结,为软件设计提供指导。由于设计模式的抽象性,初学者可能会感到难以理解。本课程旨在以简洁实用的方式,帮助同学们提升思维层次,将复杂的概念和理论简化,并通过易于理解的实例和源代码来详细解析每种设计模式。
1.3 学习设计模式的必要性
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点。
可以提高程序员的思维能力、编程能力和设计能力。
使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
成为优秀的软件工程师。
当然,软件设计模式只是一个引导。在具体的软件幵发中,必须根据设计的应用系统的特点和要求来恰当选择。对于简单的程序开发,苛能写一个简单的算法要比引入某种设计模式更加容易。但对大项目的开发或者框架设计,用设计模式来组织代码显然更好。
1.4 软件设计模式的基本要素
软件设计模式使人们可以更加简单方便地复用成功的设计和体系结构,它通常包含以下几个基本要素:模式名称、别名、动机、问题、解决方案、效果、结构、模式角色、合作关系、实现方法、适用性、已知应用、例程、模式扩展和相关模式等,其中最关键的元素包括以下 4 个主要部分。
1. 模式名称
每一个模式都有自己的名字,通常用一两个词来描述,可以根据模式的问题、特点、解决方案、功能和效果来命名。模式名称(PatternName)有助于我们理解和记忆该模式,也方便我们来讨论自己的设计。
2. 问题
问题(Problem)描述了该模式的应用环境,即何时使用该模式。它解释了设计问题和问题存在的前因后果,以及必须满足的一系列先决条件。
3. 解决方案
模式问题的解决方案(Solution)包括设计的组成成分、它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象的 组合)来解决这个问题。
4. 效果
描述了模式的应用效果以及使用该模式应该权衡的问题,即模式的优缺点。主要是对时间和空间的衡量,以及该模式对系统的灵活性、扩充性、可移植性的影响,也考虑其实现问题。显式地列出这些效果(Consequence)对理解和评价这些模式有很大的帮助。
1.5 根据目的分类
创建型模式
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF(四人组)书中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
结构型模式
用于描述如何将类或对象按某种布局组成更大的结构,GoF(四人组)书中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
行为型模式
用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。

1.6 根据作用范围来分
根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。
类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。GoF中的工厂方法、(类)适配器、模板方法、解释器属于该模式。
对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。GoF 中除了以上 4 种,其他的都是对象模式。

1.7. GoF的23种设计模式的功能简介
前面说明了 GoF 的 23 种设计模式的分类,现在对各个模式的功能进行介绍。
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
必须指出,这 23 种设计模式不是孤立存在的,很多模式之间存在一定的关联关系,在大的系统开发中常常同时使用多种设计模式,希望同学们认真学好它们。
下面是设计模式快速记忆卡片,供同学们快速浏览

