# 该文章适于已经学过GOF23种设计模式进行快速回顾或备考软件设计师快速明白各设计模式的优缺点及使用场景!

目录

1 引入

2 工厂模式

2.1 简单工厂模式

2.1.1 优缺点及适用环境

2.2 工厂方法模式

2.2.1 优缺点及适用环境

2.3 抽象工厂模式

2.3.1 优缺点及适用环境

3 单例模式

3.1 优缺点及适用环境

4 建造者模式

4.1 优缺点及适用环境

5 原型模式

5.1 优缺点及适用环境

6 适配器模式

6.1 优缺点及适用环境

7 桥接模式

7.1 优缺点及适用环境

8 组合模式

8.1 优缺点及适用环境

9 装饰模式

9.1 优缺点及适用环境

​10 外观模式

10.1 优缺点及适用环境

11 代理模式

11.1 优缺点及适用环境

12 享元模式

12.1 优缺点及适用环境

13 职责链模式

13.1 优缺点及适用环境

14 命令模式

14.1 优缺点及适用环境

15 解释器模式

15.1 优缺点及适用环境

16 迭代器模式

16.1 优缺点及适用环境

17 中介者模式

17.1 优缺点及适用环境

18 备忘录模式

18.1 优缺点及适用环境

19 观察者模式

19.1 优缺点及适用环境

20 状态模式

20.1 优缺点及适用环境

21 策略模式

21.1 优缺点及适用环境

22 模板方式模式

22.1 优缺点及适用环境

​23 访问者模式

23.1 优缺点及适用环境

注意:本文所介绍的只是设计模式的大概,只适合复习或快速了解各设计模式!若要学习设计模式,还需要用细微处学起!

 

1 引入

设计模式的定义:设计模式是在特定环境下为解决某一软件设计问题提供的一套定制的解决方案,该方案描述了类与对象之间的相互作用。

设计模式的基本要素:模式名称、问题、解决方案、效果。

设计模式分类:

1)根据用途分类

创建型:用于描述如何创建对象

结构型:用于描述如何实现类或对象的组合

行为型:描述类或对象怎样交互及怎么分配职责

2)根据范围分类

类模式:处理类和子类之间的关系,这类关系通过继承实现,在编译时就确定下来的一种静态关系。

对象模式:处理对象之间的关系,这些关系在运行时变化,更具动态性。

有个设计模式虽不属于GOF模式,但在介绍设计模式时都会说明,即 简单工厂模式。

两类分类方式一般结合使用,如单例模式是对象创建型模式,模板方法模式是类型行为模式。以下是设计模式汇总:

下面开始逐一介绍各设计模式:

2 工厂模式

2.1 简单工厂模式

概述:简单工厂模式(静态工厂方式模式),虽不属于GOF23种设计模式之一,但却是学习其他工厂模式的基础。

定义:工厂是一个用于创建其他对象的对象,从形式上讲,工厂是一个函数或方法,它返回不同原型或类型的对象。提供封装在名为工厂的类中的静态方法,以隐藏实现逻辑并使客户端代码专注于使用而不是初始化新对象。

简单工厂模式的结构:

举例:

2.1.1 优缺点及适用环境

主要优点:

  1. 客户端可免除直接创建产品对象的职责,简单工厂模式实现了对象创建和使用的分离。
  2. 客户端无需指导所创建的具体产品类的名称,只需知道具体产品类对应的参数,减少使用者记忆量。
  3. 通过引入配置文件,可不修改任何客户端代码情况下实现更换和添加新的具体产品类,提高了系统的灵活性。

主要缺点:

  1. 工厂类集中了所有产品创建逻辑,职责过重,一旦不可正常工作,整个系统受影响。
  2. 简单工厂模式会增加系统中类的个数,增加了系统复杂度和理解难度。
  3. 系统扩展困难,一旦加新产品不得不改工厂逻辑,在产品类型较多时,可能导致工厂逻辑复杂,不利于扩展与维护。(违背开闭原则)
  4. 使用静态工厂类方法,造成工厂角色无法形成基于继承的等级结构。

适用环境:

  1. 工厂类负责创建的对象较少,由于创建的对象少,不会造成工厂方法中的业务逻辑太过复杂。
  2. 客户端只知道传入工厂类的参数,对于如何创建对象不关心。

2.2 工厂方法模式

概述:简单工厂模式虽简单,但却违背了开闭原则。工厂方法模式(虚拟构造器模式/多态工厂模式)继承了简单工厂模式优点,同时做出了修改以达到符合开闭原则的要求。

定义:定义一个用于创建对象的接口,让子类决定将那一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。

工厂方法模式不再提供一个统一的工厂来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

工厂方法模式结构:

举例:

2.2.1 优缺点及适用环境

优点:

  1. 用户只需关心所需产品对应的工厂,无需关心创建细节,甚至无须知道具体产品类名。
  2. 工厂可自主确定创建何种产品对象,而如何创建这个对象的细节完全封装在具体工厂内部。
  3. 系统新加产品时,无需修改抽象工厂及抽象产品接口,无需更改客户端,也无需更改其他具体工厂及产品,只需增加一个具体工厂及具体产品。可扩展性好,完全符合 开闭原则。

缺点:

  1. 添加新产品,需编写具体产品类及工厂类,会增加系统复杂度,更多类需编译和运行,带来额外的开销。
  2. 由于考虑到系统可扩展性,引入抽象层,增加系统的抽象性与理解难度,且在实现时可能用到配置文件、反射等技术,增加了系统的实现难度。

适用:

  1. 客户端不知道它需要的对象的类。
  2. 抽象工厂类通过子类来指导创建那个对象。

2.3 抽象工厂模式

工厂方法模式引入工厂等级结构,解决简单工厂模式中工厂职责太重问题,但每个工厂只生产一类产品,导致系统存在大量工厂类,会增加系统开销。此时将一些相关的产品组成一个”产品族“,由同一个工厂来生产,这就是抽象工厂模式。

首先引入两个概念

  • 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是手机,其子类有三星、小米、华 为,则抽象手机与具体品牌的手机之间构成了一个产品等级结构,抽象手机是父类,而具体品牌的 手机是其子类。
  • 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产 品,如小米手机位于手机产品等级结构中、小米笔记本位于笔记本产品等级结构中,小米路由位于 路由产品等级结构中,这一系列电子产品就构成了一个产品族。

定义:

抽象工厂模式提供了一种方法来封装一组具有共同主题的单个工厂,而无需指定具体类。提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们具体的类。

当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更简单、有效率。

抽象工厂模式结构:

实例:

2.3.1 优缺点及适用环境

优点:

  • 隔离了具体类的生产,客户不需要指导什么被创建。
  • 当一个产品族中的多个对象被设计成一起工作时,可保证客户端始终只适用同一个产品族中的对象。
  • 增加新产品族很方便,无需修改已有系统,符合开闭原则。

缺点:

  • 增加新的产品等级结构麻烦,需对原系统进行较大修改,违背开闭原则。

适用:

  • 用户不关心对象创建过程,将对象的创建与使用解耦。
  • 系统中有多于一个的产品族,而每次只使用其中某一产品族。
  • 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
  • 产品等级结构稳定。

3 单例模式

对于某一软件系统的某些类而言,无需创建多个实例。为节约系统资源,有时需确保系统中某个类只有唯一一个实例,当这个实例创建成功后,我们无法再创建一个同类型的其他对象,所有操作都只能基于这个唯一实例。为确保唯一性,我们用 单例模式。

定义:

软件工程中,单例模式是一种软件设计模式,它将类的实例化限制为一个对象。当需一个对象来协调整个系统的操作时,这很有用。确保某一个类只有一个实例,并提供一个全局的访问点来访问这个实例。

单例模式结构:

3.1 优缺点及适用环境

优点:

  • 提供唯一实例的受控访问,可严格控制客户怎样及何时访问。
  • 由于只存在一个对象,可节约系统资源,对于一些需频繁创建和销毁的对象单例模式无疑可提高系统的性能。
  • 允许可变数目的实例(多例模式),基于单例模式可进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例,可节省系统资源,又解决单例对象共享过多有损失性能的问题。

缺点:

  • 无抽象层,扩展很大困难。
  • 单例类职责过重,一定程度违背“单一职责原则”。
  • 若实例的共享对象长时间不被利用,系统会认为是垃圾,会自动销毁并回收资源,下次利用时,又重新实例化,这会导致共享的单例对象状态丢失。

适用:

  • 系统只需要一个实例对象。如系统要求提供一个唯一的序号生成器或资源管理器,或考虑资源消耗太大只允许创建一个对象。
  • 客户调用类的单个实例只允许一个公共访问点。

4 建造者模式

定义:建造者模式时一种对象创建软件设计模式,旨在找到伸缩构造器反模式的解决方案。

伸缩性构造的反模式:指通过构造器实现对象构建参数初始化,如果对象属性比较多,导致构造器的参 数个数不可控。

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

结构:

示例1和2:

4.1 优缺点及适用环境

优点:

  1. 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得 相同的创建过程可以创建不同的产品对象。
  2. 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或 增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
  3. 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程 更加清晰,也更方便使用程序来控制创建过程。

缺点:

  1. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大, 例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
  2. 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得 很庞大,增加系统的理解难度和运行成本。

适用:

  1. 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
  2. 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
  3. 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在 指挥者类中,而不在建造者类和客户类中。
  4. 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

 

5 原型模式

在玄幻小说中,经常会有身外化身一说:“通过本体复制出与本体一样的分身(分身具有本体一样特 性)”,在面向对象系统中也会出现类似的需求,这就是原型模式(Prototype Pattern)设计出来的目标。

定义:

原型模式是软件开发中的一种创造设计模式。当要创建的对象类型由原型实例确定时使用,该实例被克隆以生成新对象。使用原型实例指定要创建的对象的种类,并通过原型来创建新对象。

结构:

示例:

5.1 优缺点及适用环境

优点:

  • 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例 可以提高新实例的创建效率。
  • 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,将具 体原型实现类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
  • 原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级 结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
  • 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便 在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作

缺点:

  1. 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造 时,需要修改源代码,违背了“开闭原则”。
  2. 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克 隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。

适用:

  • 创建新对象成本较大(如初始化需要占用较长的时间,占用太多的CPU资源或网络资源),新的对 象可以通过原型模式对已有对象进行复制来获得,如果是相似对象,则可以对其成员变量稍作修 改。
  • 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原 型模式配合备忘录模式来实现。
  • 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合 状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便。

6 适配器模式

我的笔记本电脑的工作电压是 20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在 220V的电压下工作 ?答案是引入一个电源适配器 (AC Adapter),俗称充电器或变压器 ,有了这个 电源适配器 ,生活用电和笔记本电脑即可兼容 。

定义:

适配器模式(Adapter Pattern )可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适 配者接口和抽象目标类接口,适配器模式也叫包装器(Wrapper )模式。

在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。它通常用于使现有类与其他类一起工作,而无需修改其源代码。

将类的接口转换为客户端期望的另一个接口。适配器让那些接口不兼容的类可以一起工作

结构:

根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式 中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

示例:

6.1 优缺点及适用环境

优点:

  1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
  2. 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明 的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
  3. 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代 码的基础上增加新的适配器类,完全符合“开闭原则”。
  4. 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。(类适配器)
  5. 一个对象适配器可以把多个不同的适配者适配到同一个目标;(对象适配器)
  6. 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏替换原则”,适配者的 子类也可通过该适配器进行适配。(对象适配器)

缺点:

类适配器:

  • 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适 配者;
  • 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
  • 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限 性。

对象适配器:

  • 与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者 类的一个或多个方法,可以先做一个复杂。适配者类的子类,将适配者类的方法置换掉,然后再把适配者 类的子类当做真正的适配者进行适配,实现过程较为复杂。

适用:

  • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的 源代码。 
  • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来 引进的类一起工作。

7 桥接模式

桥接模式(Bridge Pattern)是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化 的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。 

定义:

桥接模式是软件工程中使用的一种设计模式,旨在 “将抽象与其实现分离,以便两者可以独立变化”。

桥接模式又被称为柄体模式(Handle And Body Pattern)桥接模式用一种巧妙的方式处理多层继承存 在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使 得系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。

结构:

示例:

数据持久化操作

7.1 优缺点及适用环境

桥接模式是设计Java虚拟机和实现JDBC等驱动程序的核心模式之一,应用较为广泛。在软件开发中如果 一个类或一个系统有多个变化维度时,都可以尝试使用桥接模式对其进行设计。桥接模式为多维度变化的 系统提供了一套完整的解决方案,并且降低了系统的复杂度。

优点:

  • 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关 系,使得抽象和实现可以沿着各自的维度来变化。
  • 桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非 常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
  • 桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统, 符合“开闭原则”。

缺点:

  • 桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就 针对抽象层进行设计与编程。
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正 确识别两个独立维度也需要一定的经验积累。

适用:

  • 想要拆分或重组一个具有多重功能的庞杂类(例如能与多个数据库服务器进行交互的类)
  • 希望在几个独立维度上扩展一个类
  • 需要在运行时切换不同实现方法

8 组合模式

树形结构在软件中随处可见,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结 构等等,如何运用面向对象的方式来处理这种树形结构是组合模式(Composite Pattern )需要解决的问题。

定义:

组合模式描述了在对待一组对象实例的时候,使用以单个对象实例相同的方式对待。 组合的目的是将对象“组合”成树形结构,以表示部分-整体层次结构。 通过实现组合模式,客户端可以统一对待各个对象与组合。

结构:

示例:

每个句子都由单词组成,而单词又由字符组成。这些对象中的每一个都是可打印的,它们可以在它们之前 或之后打印一些东西,就像句子总是以句号结尾,单词总是在前面有空格。

8.1 优缺点及适用环境

组合模式使用面向对象的思想来实现树形结构的构建与处理,描述了如何将容器对象和叶子对象进行递归 组合,实现简单,灵活性好。

优点:

  • 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次 的差异,方便对整个层次结构进行控制。
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结 构,简化了客户端代码。
  • 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原 则”。
  • 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归 组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。

缺点:

  • 在增加新构件时很难对容器中的构件类型进行限制。

适用:

  • 如果你需要实现树状对象结构, 可以使用组合模式。

组合模式为你提供了两种共享公共接口的基本元素类型: 简单叶节点和复杂容器。 容器中可以包含 叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构。

  • 如果你希望客户端代码以相同方式处理简单和复杂元素, 可以使用该模式。

组合模式中定义的所有元素共用同一个接口。 在这一接口的帮助下, 客户端不必在意其所使用的对 象的具体类。

9 装饰模式

装饰模式(Decorator Pattern)可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在现实生活中,这种情况也到处存在。

例 如 一 张 照 片 , 我 们 可 以 不 改 变 照 片 本 身 , 给 它 增 加 一 个 相 框 , 使 得 它 具 有 防 潮 的 功 能 , 而 且 用 户 可 以 根 据 需 要 给 它 增 加 不 同 类 型 的 相 框 , 甚 至 可 以 在 一 个 小 相 框 的 外 面 再 套 一 个 大 相 框 。

装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象 之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原 有类的方法,还可以增加新的方法,以扩充原有类的功能。

定义:

在面向对象编程中,装饰模式是一种设计模式,它允许将行为静态或动态地添加到单个对象中,而不会 影响同一类中其他对象的行为。装饰器模式对于遵守单一责任原则通常是有用的,因为它允许将功能划 分到具有独特关注区域的类之间。

动态的给一个对象增加一些额外的职责。就扩展功能而言,装饰器模式提供了一种比使用子类更灵活的 方式。

装饰模式也叫包装(Wrapper)模式,它可以在不创建更多子类的情况下让对象功能得以扩展。

结构:

示例:

通常情况下,巨魔是徒手战斗的,在某些情况下我们需要给它穿戴上武器或防具时候,不可能把巨魔杀了 重新造一个吧。 

9.1 优缺点及适用环境

优点:

缺点:

适用:

10 外观模式

外观模式(Facade Pattern)是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化 客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度, 且客户端调用非常方便。

定义:

外观是一个对象,它为更大的代码 (例如类库) 提供了简化的接口。为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统 更加容易使用。

外观模式又称为门面模式,它是一种对象结构型模式。外观模式是迪米特法则的一种具体实现,通过引入 一个新的外观角色可以降低原有系统的复杂度,同时降低客户类与子系统的耦合度。

结构:

示例:

10.1 优缺点及适用环境

优点:

缺点:

适用:

11 代理模式

定义:

代理,以其最一般的形式,是一个类,用作其他东西的接口。代理是一个包装器或代理对象,客户端正 在调用它来访问幕后的真实服务对象。代理的使用可以简单地转发到真实对象,或者可以提供额外的逻 辑。在代理中,可以提供额外的功能,例如,当对真实对象的操作是资源密集型时缓存,或者在调用对 真实对象的操作之前检查前提条件。

给某一对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

控制对于某个对象的访问必要性

比如有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。

结构:

与其他设计模式关联:

示例:

11.1 优缺点及适用环境

代理模式是常用的结构型设计模式之一,它为对象的间接访问提供了一个解决方案,可以对对象的访问进行控制。代理模式类型较多,其中远程代理、虚拟代理、保护代理等在软件开发中应用非常广泛。在Java RMI、EJB、WEB Service、Spring AOP等技术和框架中都使用了代理模式。

优点:

缺点:

适用:

12 享元模式

当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题。享元模式(Flyweight Pattern)是一种解决方案,它通过共享技术实现相同或相似的细粒度的对象复用,从而 节约内存空间,提高系统性能。

定义:

享元是一种通过与其他类似对象共享尽可能多的数据来最小化内存使用的对象;当简单的重复表示将使 用不可接受的内存量时,这是一种使用大量对象的方法。

使用共享可以有效地支持大量细粒度对象。

举例:

有一个粒子系统

在游戏运转的时候,战斗单位不断的产生粒子,会导致内存占用持续增加,因为一个粒子里面包好了非常 占用内存的精灵图片,而不巧的是每个相同类型的粒子里面使用精灵图片是相同的,导致相同的资源重复 占用内存资源,为了解决这个问题可以使用享元模式来解决。

通过观察Particle对象我们可以发现颜色和精灵是在同一个粒子对象里面都是一样的,对这些不变的量我 们可以称之为内在状态,对于坐标、方向、速度这些在外部进行变动的我们称之为外在状态。 

从粒子类中抽出外在状态, 那么我们只需三个不同的对象 (子弹、 导弹和弹片) 就能表示游戏中的所有 粒子。 我们将这样一个仅存储内在状态的对象称为享元。

结构:

与其他设计模式:

12.1 优缺点及适用环境

优点:

缺点:

适用:

13 职责链模式

在很多情况下处理某个请求的对象不止一个,例如大学里的奖学金审批,学长在向辅导员提交审批表之后 首先是辅导员签字审批,然后交给系主任审批签字,接着院长审批,最后可能是校长来审批,在这个过程 中奖学金申请表可以看出一个请求对象,而不同级别的审批者都可以处理该请求对象,除了辅导员外,学 生不需要一一与其他审批者交互,等待结果即可。在审批过程中如果有一个审批者认为不符合,则请求终 止;否则继续传递给下一个审批者,直到最后一个审批者来确定是否授予奖学金。

这一个过程构成了处理申请表的链式结构,申请表沿着这条链进行传递,这条链我们可以称为职责链。

职责链可以是直线型、环形也可以是树形结构,最常见的链式直线型。

定义:

责任链模式是一种设计模式,由一个命令对象源和一系列处理对象组成。每个处理对象都包含定义它可 以处理的命令对象类型的逻辑;其余的被传递给链中的下一个处理对象。

避免一个请求对象的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接收请求的对象连 接成一个链,并且沿着这条链传递请求,直到有一个对象能够处理它为止。

结构:

示例:

13.1 优缺点及适用环境

职责链模式通过建立一条链来组织请求的处理者,请求将沿着链进行传递,请求发送者无须知道请求在何 时、何处以及如何被处理,实现了请求发送者与处理者的解耦。在软件开发中,如果遇到有多个对象可以 处理同一请求时可以应用职责链模式,例如在Web应用开发中创建一个过滤器Filter)链来对请求数据进行 过滤,在工作流系统中实现公文的分级审批等等,使用职责链模式可以较好地解决此类问题。

优点:

缺点:

适用:

14 命令模式

在软件开发中,我们经常需要向某些对象发送请求(调用其中的某个或某些方法),但是并不知道请求的 接收者是谁,也不知道被请求的操作是哪个,此时,我们特别希望能够以一种松耦合的方式来设计软件, 使得请求发送者与请求接收者能够消除彼此之间的耦合,让对象之间的调用关系更加灵活,可以灵活地指 定请求接收者以及被请求的操作。命令模式(Command Pattern)为此类问题提供了一个较为完美的解 决方案。

定义:

命令模式是一种行为设计模式,其中对象用于封装执行操作或稍后触发事件所需的所有信息。

将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化,对请求排队或者记录请求 日志,以及支持可撤销的操作。

命令模式的别名为动作Action)模式或事务Transaction)模式。 

举例:

假如你正在开发一款新的文字编辑器, 当前的任务是创建一个包含多个按钮的工具栏, 并让每个按钮对 应编辑器的不同操作。 你创建了一个非常简洁的 按钮类, 它不仅可用于生成工具栏上的按钮, 还可用于 生成各种对话框的通用按钮。

尽管所有按钮看上去都很相似, 但它们可以完成不同的操作 (打开、 保存、 打印和应用等)。你可能使 用继承去处理具体的按钮操作逻辑。

这种方式有严重缺陷。 首先, 你创建了大量的子类, 当每次修改基类 按钮时, 你都有可能需要修改所有 子类的代码。再次,可能会存在多个类实现同一个功能的现象(如:保存按钮、保存菜单项按钮,保存快 捷键等等),需要复制大量的代码。

怎么解决呢?那就是将GUI和业务逻辑功能剥离开来,通过传递数据参数完成交互。

命令模式建议 GUI 对象不直接提交这些请求。 你应该将请求的所有细节 (例如调用的对象、 方法名称和 参数列表) 抽取出来组成命令类, 该类中仅包含一个用于触发请求的方法。

命令对象负责连接不同的 GUI 和业务逻辑对象。 此后, GUI 对象无需了解业务逻辑对象是否获得了请 求, 也无需了解其对请求进行处理的方式。 GUI 对象触发命令即可, 命令对象会自行处理所有细节工作。如下面的设计方式。 

应用命令模式后,我们不再需要任何按钮子类来实现点击行为。 我们只需在 按钮Button基类中添加一个 成员变量来存储对于命令对象的引用, 并在点击后执行该命令即可。

结构:

示例:

14.1 优缺点及适用环境

优点:

缺点:

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个对请求接收者的调用操作都需要 设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

适用:

15 解释器模式

解释器模式(nterpreter Pattern)用于描述如何构成一个简单的语言解释器,主要应用于使用面向对象 语言开发的解释器设计,所以在实际开发中很少使用到。

定义:

在计算机编程中,解释器模式是一种设计模式,它指定如何评估语言中的句子。基本思想是用专门的计 算机语言为每个符号 (终端或非终端) 设置一个类。语言中句子的语法树是组合模式的实例,用于评估 (解释) 客户端的句子。

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的 句子。

在解释器模式的定义中所指的"语言"是使用规定格式和语法的代码,就像C、Java和C#等语言无法直接 解释类似“1 + 2 – 3+ 4”这样的字符串(如果直接作为数值表达式时可以解释),我们必须自己定义一 套文法规则来实现对这些语句的解释,即设计一个自定义语言。

结构:

15.1 优缺点及适用环境

优点:

缺点:

适用:

16 迭代器模式

定义:

迭代器模式是一种设计模式,其中迭代器用于遍历容器并访问容器的元素。

提供一种方法顺序访问一个聚合对象中的各个元素,而又不用暴露该对象的内部表示。

举例:

无论集合的构成方式如何, 它都必须提供某种访问元素的方式, 便于其他代码使用其中的元素。 集合 应提供一种能够遍历元素的方式, 且保证它不会周而复始地访问同一个元素。

如果你的集合基于列表,遍历实现非常简单,如果是复杂的数据结构(如树),那就不是那么容易,比 如:

今天需要深度优先算法来遍历树结构;

明天需要广度优先算法来遍历树结构;

下周则可能会需要其他方式 (比如随机存取树中的元素)。 

不断向集合中添加遍历算法会模糊其 “高效存储数据” 的主要职责。 此外, 有些算法可能是根据特定应 用订制的, 将其加入泛型集合类中会显得非常奇怪。

另一方面, 使用多种集合的客户端代码可能并不关心存储数据的方式。 不过由于集合提供不同的元素 访问方式, 你的代码将不得不与特定集合类进行耦合。

解决方案:将集合的遍历行为抽取为单独的 迭代器 对象。

类比现实世界

比如大家想要去某个城市去旅游,这个城市有许多旅游景点,旅游方式可以采用下面的方法。

  • 采用自由漫步的方式,一个景点一个景点的去旅游,中途可能会出现绕了很大一个圈子找不到某 个旅游景点的情况;
  • 也可以买一个便宜的智能虚拟导游程序;
  • 还可以雇佣一个对本城市了如指掌的导游,帮你安排行程以及景点激动人心的故事讲解。

自由漫步、虚拟导游、真人导游就是这个由众多景点组合成的集合的迭代器。

结构:

示例:

使用迭代器实现对宝箱中物品的遍历。

16.1 优缺点及适用环境

由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,我们只需要直接使用Java、C# 等语言已定义好的迭代器即可,迭代器已经成为我们操作聚合对象的基本工具之一。

优点:

缺点:

适用:

 

17 中介者模式

为了有一个好的面向对象的设计,我们必须创建许多相互交互的类。如果不应用某些原则,最终的框架将 以一团糟而告终,其中每个对象都依赖于许多其他对象才能运行。为了避免紧密耦合的框架,我们需要一 种机制来促进对象之间的交互,其方式是对象不知道其他对象的存在。

对于那些对象之间存在复杂交互关系的系统,中介者模式(Mediator Pattern)提供了一种简单化复杂交 互的解决方案。

定义:

中介模式定义了一个对象,该对象封装了一组对象如何交互。使用中介模式,对象之间的通信被封装在 中介对象中。对象不再直接相互通信,而是通过中介器进行通信。这减少了通信对象之间的依赖性,从 而减少了耦合。

用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使 其耦合松散,而且可以独立地改变它们之间的交互。

通过引入中介者来简化对象之间的复杂交互,中介者模式是“迪米特法则”的一个典型应用。

结构:

示例:

17.1 优缺点及适用环境

中介者模式在事件驱动类软件中应用较为广泛,特别是基于GUI(GraphicaL User Interface,图形用户 界面)的应用软件,此外,在类与类之间存在错综复杂的关联关系的系统中,中介者模式都能得到较好的 应用。

优点:

缺点:

一段时间后, 中介者可能会演化成为上帝对象。

适用:

18 备忘录模式

定义:

备忘录模式是一种软件设计模式,它提供了将对象恢复到其先前状态(通过回滚撤消)的能力。

在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对 象恢复到原先的状态。

备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态 无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,当前很多软件都提供了撤销Undo)操 作,其中就使用了备忘录模式。备忘录模式又称为标记(Token)模式。

结构:

示例:

模拟五子棋的悔棋。

18.1 优缺点及适用环境

备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现 在很多基于窗体和浏览器的应用软件并没有提供撤销操作。

优点:

缺点:

资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间, 每保存一次对象的状态都需要消耗一定的系统资源。

适用:

19 观察者模式

在软件系统中对象并不是孤立存在的,一个对象行为的改变可能会导致一个或多个其他与之存在依赖关系 行为发生改变,它们之间将会产生联动,“ 触一发而动百发”。为了更好的描述对象之间存在的这种一对多 的联动,产生了观察者模式(Observer Pattern)这一解决方案。

定义:

观察者模式是一种软件设计模式,其中一个称为主体的对象维护其依赖项列表,称为观察者,并通常通 过调用其方法之一自动将任何状态更改通知它们。

定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时其相关依赖对象都得到通知 并被自动更新。

在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多 个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易 于扩展。

观察者模式的别名包括:发布-订阅模式、模型-视图模式、源- 监听器模式、从属者模式。

结构:

示例:

19.1 优缺点及适用环境

观察者模式是一种使用频率非常高的设计模式,无论是移动应用、Web应用或者桌面应用,观察者模式几 乎无处不在,它为实现对象之间的联动提供了一套完整的解决方案,凡是涉及到一对一或者一对多的对象 交互场景都可以使用观察者模式。

优点:

缺点:

适用:

20 状态模式

在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不 同的状态下也将具有不同的行为。如果使用复杂的条件判断语句(如if或switch)来进行状态的判断和转 换操作,这会导致代码的可维护性和灵活性下降,特别是出现新状态的时候代码的扩展性很差,客户端代 码也需要进行修改,违反开闭原则。为了更好地对这些具有多种状态的对象进行设计,我们可以使用一种 被称之为状态模式(State Pattern )的设计模式。

定义:

状态模式是一种行为软件设计模式,它允许对象在其内部状态发生变化时改变其行为。这种模式接近于 有限状态机的概念。状态模式可以解释为策略模式,它能够通过调用模式接口中定义的方法来切换策 略。

允许对象在其内部状态更改时更改其行为。对象将看起来像修改了它的类。

状态模式又名状态对象(Object for States )。

结构:

示例:

模拟游戏角色状态切换:

20.1 优缺点及适用环境

在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例 如公文状态的转换、游戏中角色状态切换等。

优势:

缺点:

适用:

21 策略模式

定义:

在计算机编程中,策略模式(也称为政策模式)是一种行为软件设计模式,可以在运行时选择算法。

定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它 的客户端而变化。

每一个封装算法的类都可以称为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象 的策略类来做算法声明。

结构:

示例:

模拟一个探险小游戏。

21.1 优缺点及适用环境

策略模式用于算法的自由切换和扩展,它是应用较为广泛的设计模式之一。策略模式对应于解决某一问题 的一个算法族,允许用户从该算法族中任选一个算法来解决某一问题,同时可以方便地更换算法或者增加 新的算法。只要涉及到算法的封装、复用和切换都可以考虑使用策略模式。

优点:

缺点:

适用:

22 模板方式模式

定义:

模板方法是超类(通常是抽象超类)中的一种方法,并定义了一个对一系列高层步骤的操作框架。这些 步骤本身是由与模板方法相同的类中的其他帮助程序方法实现的。

定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法 的结构即可重新定义该算法的某些特定步骤。

在模板方法模式中,将实现功能的每一个步骤所对应的方法称为基本方法(例如“邀请”、“接待”),而调 用这些基本方法同时定义基本方法的执行次序的方法称为模板方法(例如“请客吃饭”)。通过使用模板方 法模式,一方面提高了代码的复用性,另一方面还可以利用面向对象的多态性,在运行时选择一种具体子 类,实现完整的“请客吃饭” 方法,提高系统的灵活性和可扩展性。

模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式。

结构:

示例:

模拟旅行过程:

22.1 优缺点及适用环境

模板方法模式是基于继承的代码复用技术,它体现了面向对象的诸多重要思想,是一种使用较为频繁的模式。模板方法模式广泛应用于框架设计中,以确保通过父类来控制处理流程的逻辑顺序(如框架的初始化,测试流程的设置等)。

优点:

缺点:

模板方法中的步骤越多, 其维护工作就可能会越困难,此时,可结合桥接模式来进行设计。

适用:


23 访问者模式

为了解决这类问题,就产生了访问者模式(Visitor Pattern ),其定义如下:

访问者设计模式是一种将算法与其运行的对象结构分开的方法。这种分离的实际结果是能够在不修改结 构的情况下向现有对象结构添加新操作。

提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作 用于这些元素的新操作。

在使用访问者模式时,被访问元素通常不是单独存在的,它们存储在一个集合中,这个集合被称为“ 对象结 构”,访问者通过遍历对象结构实现对其中存储的元素的逐个操作。

访问者模式在遍历对象结构的时候可以采用迭代器实现。

结构:

这种调用机制也称为“双重分派”,正因为使用了双重分派机制,使得增加新的访问者无须修改现有类库代 码,只需将新的访问者对象作为参数传入具体元素对象的accept()方法,程序运行时将回调在新增Visitor 类中定义的visit()方法,从而增加新的元素访问方式。

对象结构是一个集合,它用于存储元素对象并接受访问者的访问。

示例:

app中创建一个报告模块来统计一组用户信息,要求尽可能详细:

23.1 优缺点及适用环境

由于访问者模式的使用条件较为苛刻,本身结构也较为复杂,因此在实际应用中使用频率不是特别高。当 系统中存在一个较为复杂的对象结构,且不同访问者对其所采取的操作也不相同时,可以考虑使用访问者 模式进行设计。在XML文档解析、编译器的设计、复杂集合对象的处理等领域访问者模式得到了一定的应 用。

优点:

缺点:

适用:

注意:本文所介绍的只是设计模式的大概,只适合复习或快速了解各设计模式!若要学习设计模式,还需要用细微处学起!

 

 

更多推荐