0%

官方系统架构设计师教程-ch7-设计模式

设计模式

近年来,在面向对象领域中的一个重要突破就是提出了设计模式的概念。软件的设计模式是人们在长期的开发实践中良好经验的结晶,它提供了一个简单、统一的描述方法,使人们可以复用这些软件设计方法、过程管理经验。由于设计模式在表达上既经济又清楚,从而越来越受到重视。本章将介绍软件设计模式的概念、组成要素和分类,并介绍了façade、Adapter、Abstract Factory等常用设计模式。

设计模式概述

在任何设计活动中都存在着某些重复遇到的典型问题,不同开发人员对这些问题设计出不同的解决方案,随着设计经验在实践者之间日益广泛地被利用,描述这些共同问题和解决这些问题的方案就形成了所谓的模式。

设计模式的历史

模式概念是建筑师Christopher Alexander提出的,他提出可以把现实中一些已经实现的较好的建筑和房屋的设计经验作为模式,在以后的设计中直接加以运用。他还定义了一种“模式语言”来描述建筑和城市中的成功的架构。

Christopher Alexander将模式分为几个部分:首先是特定的情景(Context),指模式在何种状况下发生作用;其二是动机(System of Force),指问题或预期的目标;其三是解决方案(Solution),指平衡各动机或解决所阐述问题的一个构造或配置。他提出模式是表示特定的情景、动机、解决方案三个方面关系的一个规则,每个模式描述了一个在某种特定情景下不断重复发生的问题,以及该问题解决方案的核心所在。模式既是一个事物又是一个过程,不仅描述该事物本身,而且提出了通过怎样的过程来产生该事物。设计模式的核心是问题描述和解决方案,问题描述说明模式的最佳使用场合及它将如何解决问题,解决方案是用一组类和对象及其结构和动态协作来描述的。

20世纪80年代中期由Ward Cunningham和Kent Beck将其思想引入到软件领域。1995年,E. Gamma,R. Helm,R. Johnson和J. Vlissides4人合著了Design Patterns; Elements of Object-Oriented Software,这是软件设计模式领域中的一本经典书籍,从此设计模式成为软件工程领域内的一个重要研究领域,这四人也因此被称为Gang of Four(GoF),成为设计模式中的大师级人物。

为什么要使用设计模式

面向对象设计时需要考虑许多因素,例如封装性、粒度大小、依赖关系、灵活性和可重用性等。如何确定系统中类及类之间的关系?如何保证在系统内部的一个类始终只有一个实例被创建?如何动态地将追加的功能增加到一个对象?哪些是设计时要努力达到的目标?这些都是软件设计中不容易掌握的问题。要真正掌握软件设计,必须研究其他软件设计大师的设计,这些设计中包含了许多设计模式。软件模式的应用对软件开发产生了重大的作用,主要表现在以下几个方面。

简化并加快设计

开发人员面对的问题来自不同的层次。在最底层,涉及的是单个类的接口或实现的细节问题;在最高层,涉及的是系统的整体架构的创建问题。设计模式关注的是中间层,在这一层必须保证局部化的特定的设计性质。设计模式使得软件开发人员无须从底层做起,开发人员可以重用成功的设计,可节省开发时间,同时有助于提高软件质量。

方便开发人员之间的通信

利用设计模式可以更准确地描述问题及问题的解决方案,使解决方案具有一致性;也有利于开发人员可以在更高层次上思考问题和讨论方案。例如,如果所有人都理解Factory设计模式的意思,则开发人员可以用“建议采用Factory设计模式来解决这个问题”这样的话来表达。

降低风险

由于设计模式经过很多人的使用,已被证明是有效的解决方法,所以采用设计模式可以降低失败的可能性,也有利于在复杂的系统中产生简洁、精巧的设计。

有助于转到面向对象技术

新技术要在一个开发机构中得到应用,一般要经历两个阶段,即技术获取阶段和技术迁移阶段。技术获取阶段较容易,但在技术迁移阶段,由于开发人员对新技术往往会有抵触或排斥心理,对新技术可能带来的效果持怀疑态度,同时由于对新技术还是一知半解,所以要在一个开发机构中进行技术迁移并不是一件容易的事。设计模式一般都是基于面向对象技术而提出的,也可应用于接口定义良好的结构化方法中。另外,设计模式是可重用的设计经验的总结,已在实际的系统中多次得到成功应用,因此通过对设计模式的研究,能够深入理解良好设计的最基本的性质,从而有助于说服开发人员采用新技术。

成熟的软件设计模式具有以下特性。

(1)巧妙:设计模式是一些优雅的解决方案,是在大量实践经验的基础上提炼出来的。

(2)通用:设计模式通常不依赖于某个特定的系统类型、程序设计语言或应用领域,它们是通用的。

(3)得到很好的证明:设计模式在实际系统和面向对象系统中得到广泛应用,它们并不仅仅停留在理论上。

(4)简单:设计模式通常都非常简单,只涉及很少的一些类。为了构建更多更复杂的解决方案,可以把不同的设计模式与应用代码结合或混合使用。

(5)可重用:设计模式的建档方式使它们非常易于使用,因而可方便用于任何适宜的系统。

(6)面向对象:设计模式是用最基本的面向对象机制如类、对象、多态等构造的。许多模式特别强调了某些面向对象设计擅长的领域,例如,区分接口和实现、降低各部分之间的依赖性、隔离硬件和软件等。

设计模式的组成元素

模式是一个高度抽象的概念。设计模式的基本组成元素如下。

模式名

模式必须具有一个有意义的名称,这样就可以用一个词或短语来指代该模式,以及它所描述的知识和结构。模式名称简洁地描述了模式的本质。模式名可以帮助我们思考,便于我们与其他人交流设计思想及设计结果,找到恰当的模式名也是设计模式编目工作的难点之一。

问题或意图

陈述问题并描述它的意图,以及它在特定的情景和动机下要达到的目标,它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等,也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。通常情况下这些动机和目标是相互矛盾、相互影响的。

情景

情景是问题及其解决方案产生时的前提条件。情景告诉我们该模式的适用性,可以将情景视为应用该模式之前的系统初始配置。

动机

它描述相关的动机和约束,它们之间或与期望达到的目标之间的相互作用(或冲突),通常需要对各期望的目标进行优先级排序。动机阐明了问题的复杂性,定义了在相互冲突时所采取的各种权衡手段。一个好的模式应尽可能将所有产生影响的动机考虑在内。

解决方案

解决方案是描述一些静态的关系和动态的规则,用以描述如何得到所需的结果。通常是给出一组指令来说明如何构造所需的工作制品。该说明可包括图表、文字,用以标示模式的结构、参与者及其之间的协作,从而表明问题是如何解决的。因为模式就像一个模板,可应用于多种不同的场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。

示例

示例指一个或多个该模式的应用例子,示例说明了模式在怎样的初始情景下如何发生作用,如何改变情景而导致结果情景的出现。示例帮助读者理解模式的具体使用方法。

结果情景

结果情景指在应用该模式后系统的状态或配置,包括模式发生作用后带来的后果,以及在新的情景下产生的问题、可应用的模式等。它阐述了模式的后续状态和副作用。通常通过对结果情景的描述,使该模式与其他模式联系起来(该模式的结果情景成为其他模式的初始情景)。

基本原理

基本原理指对该模式中的解决步骤或采用的规则的解释、证明,解释该模式如何、为何能解决当前问题,它采用的方法为何能得到与期望相一致的结果。

相关模式

该模式与其他模式的关系,包括静态的和动态的。例如,该模式的前导模式(前导模式应用后产生的结果情景与该模式的初始情景一致)、后续模式(该模式应用后产生的结果情景与后续模式的初始情景一致)、替代模式(使用该模式的替代模式产生同样的效果)等。

已知应用

阐述该模式在已有应用系统中的实际应用情况,有助于验证该模式的有效性。尽管我们描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。模式效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。

通常好的模式前面都有一个摘要,提供简短的总结和概述,为模式描绘出一个清晰的图画,提供有关该模式能够解决问题的快速信息。有时这种描述称为模式的缩略概要,或一个缩略图。模式应该说明它的目标读者,以及对读者有哪些知识要求。

设计模式的分类

软件模式主要可分为设计模式、分析模式、组织和过程模式等,每一类又可细分为若干个子类。在此着重介绍设计模式,目前它的使用最为广泛。设计模式主要用于得到简洁灵活的系统设计,GoF的书中共有23个设计模式,这些模式可以按两个准则来分类:一是按设计模式的目的划分,可分为创建型、结构型和行为型三种模式;二是按设计模式的范围划分,即根据设计模式是作用于类还是作用于对象来划分,可以把设计模式分为类设计模式和对象设计模式。

创建型模式

该类型模式是对对象实例化过程的抽象,它通过采用抽象类所定义的接口,封装了系统中对象如何创建、组合等信息。

结构型模式

该类模式主要用于如何组合已有的类和对象以获得更大的结构,一般借鉴封装、代理、继承等概念将一个或多个类或对象进行组合、封装,以提供统一的外部视图或新的功能。

行为型模式

该类模式主要用于对象之间的职责及其提供的服务的分配,它不仅描述对象或类的模式,还描述它们之间的通信模式,特别是描述一组对等的对象怎样相互协作以完成其中任一对象都无法单独完成的任务。

设计模式实例

创建性模式

在系统中,创建性模式支持对象的创建。该模式允许在系统中创建对象,而不需要在代码中标识特定类的类型,这样用户就不需要编写大量、复杂的代码来初始化对象。它是通过该类的子类来创建对象的。但是,这可能会限制在系统内创建对象的类型或数目。本节将介绍如下的创建性模式:

  • Abstract Factory(抽象工厂)。
  • Builder(构建器)。
  • Factory Method(工厂方法)。
  • Prototype(原型)。
  • Singleton(单例)。
Abstract Factory模式

在不指定具体类的情况下,这种模式为创建一系列相关或相互依赖的对象提供了一个接口。根据给定的相关抽象类,Abstract Factory模式提供了从一个相匹配的具体子类集创建这些抽象类的实例的方法,如图7-1所示。

Abstract Factory模式提供了一个可以确定合适的具体类的抽象类,这个抽象类可以用来创建实现标准接口的具体产品的集合。客户端只与产品接口和Abstract Factory类进行交互。使用这种模式,客户端不用知道具体的构造类。Abstract Factory模式类似于Factory Method模式,但是Abstract Factory模式可以创建一系列的相关对象。

其优点如下。

  • 可以与具体类分开。
  • 更容易在产品系列中进行转换。
  • 提高了产品间的一致性。
图7-1 Abstract Factory模式找不到图片(Image not found)

在以下情况中,应该使用Abstract Factory模式:

  • 系统独立于产品的创建、组成以及表示。
  • 系统配置成具有多个产品的系列,例如Microsoft Windows或Apple McIntosh类。
  • 相关产品对象系列是共同使用的,而且必须确保这一点。这是该模式的关键,否则可以使用Factory Method模式。
  • 你希望提供产品的类库,只开放其接口,而不是其实现。
Builder模式

Builder模式将复杂对象的构建与其表示相分离,这样相同的构造过程可以创建不同的对象。通过只指定对象的类型和内容,Builder模式允许客户端对象构建一个复杂对象。客户端可以不受该对象构造的细节的影响。这样通过定义一个能够构建其他类实例的类,就可以简化复杂对象的创建过程。Builder模式生产一个主要产品,而该产品中可能有多个类,但是通常只有一个主类。图7-2所示的就是Builder模式,当使用该模式时,可以一次就创建所有的复杂对象。而其他模式一次就只能创建一个对象。

其优点如下。

  • 可以对产品的内部表示进行改变。
  • 将构造代码与表示代码相分离。

在以下情况中,应该使用Builder模式:

  • 创建复杂对象的算法独立于组成对象的部分以及这些部分的集合方式。
  • 构造过程必须允许已构建对象有不同表示。
图7-2 Builder模式找不到图片(Image not found)
Factory Method模式

Factory Method模式定义了创建对象的接口,它允许子类决定实例化哪个类。它允许类将实例化工作交给其子类,这对于在特定目的下构建单个对象是非常有帮助的,而且它不需要请求者知道要被实例化的特定类,这就可以在不修改代码的情况下引入新类,因为新类只实现了接口,这样它就可以被客户端使用。可以创建一个新的Factory类来创建新类,而由这个Factory类来实现Factory接口。图7-3所示的是Factory Method模式。

图片详情找不到图片(Image not found)

其优点如下。

● 没有了将应用程序类绑定到代码中的要求,代码只处理接口,因此可以使用任何实现了接口的类。

● 允许子类提供对象的扩展版本,因为在类中创建对象比直接在客户端创建要更加 灵活。

在以下情况中,应该使用Factory Method模式:

● 类不能预料它必须创建的对象的类。

● 类希望其子类指定它要创建的对象。

● 类将责任转给某个帮助子类,而用户希望定位那个被授权的帮助子类。

Prototype模式

Prototype模式允许对象在不了解要创建对象的确切类以及如何创建等细节的情况下创建自定义对象。使用Prototype实例,便指定了要创建的对象类型,而通过复制这个Prototype,就可以创建新的对象。Prototype模式是通过先给出一个对象的Prototype对象,然后再初始化对象的创建。创建初始化后的对象再通过Prototype对象对其自身进行复制来创建其他对象。Prototype模式使得动态创建对象更加简单,只要将对象类定义成能够复制自身就可以实现。图7-4所示的是Prototype模式。

图7-4 Prototype模式找不到图片(Image not found)

其优点如下。

● 可以在运行时添加或删除产品。

● 通过改变值指定新对象。

● 通过改变结构指定新对象。

● 减少子类的生成和使用。

● 可以用类动态地配置应用程序。

在以下情况中,应该使用Prototype模式:

● 在运行时,指定需要例化的类,例如动态载入。

● 避免构建与产品的类层次结构相似的工厂类层次结构。

● 当类的实例是仅有的一些不同状态组合之一的时候。

Singleton模式

Singleton模式确保一个类只有一个实例,并且提供了对该类的全局访问入口,它可以确保使用这个类实例的所有的对象使用相同的实例。图7-5所示的是Singleton模式。

图7-5 Singleton模式找不到图片(Image not found)

其优点如下。

● 对单个实例的受控制访问。

● 名称空间的减少。

● 允许改进操作和表示。

● 允许可变数目的实例。

● 比类操作更灵活。

在以下情况中,应该使用Singleton模式:如只有一个类实例。

结构性模式

结构性模式控制了应用程序较大部分之间的关系。它将以不同的方式影响应用程序,例如Adapter模式允许两个不兼容的系统进行通信,而Facade模式允许在不删除系统中所有可用选项的情况下为用户提供一个简化的界面。结构性模式允许在不重写代码或自定义代码的情况下创建系统。这可以使系统具有增强的重复使用性和应用性能。本节将介绍如下的结构性模式:

● Adapter

● Bridge

● Composite

● Decorator

● Facade

● Flyweight

● Proxy

Adapter模式

Adapter模式可以充当两个类之间的媒介,它可以转换一个类的接口,这样就可以被另外一个类使用,这使得具有不兼容接口的类能够协同使用。Adapter模式实现为客户端所知的接口,并且为客户端提供对不为其所知的类实例的访问。Adapter对象可以在不知道实现该接口的类的情况下提供该接口的功能。图7-6所示的就是Adapter模式。

图7-6 Adapter模式找不到图片(Image not found)

其优点如下。

● 允许两个或多个不兼容的对象进行交互和通信。

● 提高已有功能的重复使用性。

在以下情况中,应该使用Adapter模在以下情况中,应该使用Adapter模式:

● 要使用已有类,而该类接口与所需的接口并不匹配。

● 要创建可重用的类,该类可以与不相关或未知类进行协作,也就是说,类之间并不需要兼容接口。

● 要在一个不同于已知对象接口的接口环境中使用对象。

● 必须要进行多个源之间的接口转换的时候。

Bridge模式

Bridge模式可以将一个复杂的组件分成两个独立的但又相关的继承层次结构:功能性的抽象和内部实现。改变组件的这两个层次结构很简单,以至于它们可以相互独立地变化。当具有抽象的层次结构和相应的实现层次结构时,Bridge模式是非常有用的。除了可以将抽象和实现组合成许多不同的类,该模式还可以以动态组合的独立类的形式实现这些抽象和实现。图7-7所示的是Bridge模式。

图7-7 Bridge模式找不到图片(Image not found)

其优点如下。

● 可以将接口与实现相分离。

● 提高了可扩展性。

● 对客户端隐藏了实现的细节。

在以下情况中,应该使用Bridge模式:

● 想避免在抽象及其实现之间存在永久的绑定。

● 抽象及其实现可以使用子类进行扩展。

● 抽象的实现被改动应该对客户端没有影响;也就是说,你不用重新编译代码。

Composite模式

Composite模式允许创建树型层次结构来改变复杂性,同时允许结构中的每一个元素操作同一个接口。该模式将对象组合成树型结构来表示整个或部分的层次结构。这就意味着Composite模式允许客户端使用单个对象或多个同一对象的组合。图7-8所示的是Composite模式。

图7-8 Composite模式找不到图片(Image not found)

其优点如下。

● 定义了由主要对象和复合对象组成的类层次结构。

● 使得添加新的组件类型更加简单。

● 提供了结构的灵活性和可管理的接口。

在以下情况中,应该使用Composite模式:

● 想要表示对象的整个或者部分的层次结构。

● 想要客户端能够忽略复合对象和单个对象之间的差异。

● 结构可以具有任何级别的复杂性,而且是动态的。

Decorator模式

Decorator模式可以在不修改对象外观和功能的情况下添加或者删除对象功能。它可以使用一种对客户端来说是透明的方法来修改对象的功能,也就是使用初始类的子类实例对初始对象进行授权。Decorator模式还为对象动态地添加了额外的责任,这样就在不使用静态继承的情况下,为修改对象功能提供了灵活的选择。图7-9所示的是Decorator模式。

其优点如下。

● 比静态继承具有更大的灵活性。

● 避免了特征装载的类处于层次结构的过高级别。

● 简化了编码,因为用户编写的每一个类都针对功能的一个特定部分,而不用将所有的行为编码到对象中。

● 改进了对象的扩展性,因为用户可以通过编写新的类来作出改变。

在以下情况中,应该使用Decorator模式:

● 想要在单个对象中动态并且透明地添加责任,而这样并不会影响其他对象。

● 想要在以后可能要修改的对象中添加责任。

● 当无法通过静态子类化实现扩展时。

图7-9 Decorator模式找不到图片(Image not found)
Facade模式

Facade模式为子系统中的一组接口提供了一个统一的接口。因为只有一个接口,该模式就定义了更容易使用子系统的高级接口。这个统一的接口允许对象使用该接口与子系统进行通信,从而实现对子系统的访问。图7-10所示的是Facade模式。

图7-10 Facade模式找不到图片(Image not found)

其优点如下。

● 在不减少系统所提供的选项的情况下,为复杂系统提供了简单接口。

● 对客户端屏蔽了子系统组件。

● 提高了子系统与其客户端之间的弱耦合度。

● 如果每一个子系统使用自身的Facade模式而且系统的其他部分也使用Facade模式与子系统进行通信的话,就可以降低子系统之间的耦合度。

● 将客户端请求转换后发送给能够处理这些请求的子系统。

在以下情况中,应该使用Facade模式:

● 想要为复杂的子系统提供简单的接口。

● 在客户端和抽象的实现类中存在许多依赖关系。

● 想要对子系统进行分层

Flyweight模式

Flyweight模式可以通过共享对象减少系统中低等级的、详细的对象数目。如果一个类实例包含用来互换使用的相同信息,Flyweight模式允许程序通过共享一个接口来避免使用多个具有相同信息的实例所带来的开销。图7-11所示的是Flyweight模式。

图7-11 Flyweight模式找不到图片(Image not found)

其优点如下。

● 减少了要处理的对象数目。

● 如果对象能够持续,可以减少内存和存储设备。

在以下情况中,应该使用Flyweight模式:

● 应用程序使用大量的对象。

● 由于对象数目巨大,导致很高的存储开销。

● 应用程序不依赖于对象的身份。

Proxy模式

Proxy模式为控制对初始对象的访问提供了一个代理或者占位符对象。它的实现可以有多种类型,其中Remote Proxy(远程代理)和Virtual Proxy(虚拟代理)是最常见的。图7-12所示的是Proxy模式。

图7-12 Proxy模式找不到图片(Image not found)

其优点如下。

● 远程代理可以隐藏对象位于不同的地址空间的事实。

● 虚拟代理可以执行优化操作,例如根据需要创建一个对象。

在以下情况中,应该使用Proxy模式,如需要比简单的指针更灵活、更全面的对象引用。

行为性模式

行为性模式可以影响一个系统的状态和行为流。通过优化状态和行为流转换和修改的方式,可以简化、优化并且提高应用程序的可维护性。本节将介绍如下的行为性模式:

● Chain of Responsibility

● Command

● Interpreter

● Iterator

● Mediator

● Memento

● Observer

● State

● Strategy

● Template Method

● Visitor

Chain of Responsibility模式
  • 责任链模式的参考文献,应用:浏览器中的事件有冒泡机制

Chain of Responsibility模式可以在系统中建立一个链,这样消息可以在首先接收到它的级别处被处理,或者可以定位到可以处理它的对象。图7-13所示的是Chain ofResponsibility模式。

图7-13 Chain of Responsibility模式找不到图片(Image not found)

其优点如下。

● 降低了耦合度。

● 增加向对象指定责任的灵活性。

● 由于在一个类中产生的事件可以被发送到组成中的其他类处理器上,类的集合可以作为一个整体。

在以下情况中,应该使用Chain of Responsibility模式:

● 多个对象可以处理一个请求,而其处理器却是未知的。

● 想要在不指定确切的请求接收对象的情况下,向几个对象中的一个发送请求。

● 可以动态地指定能够处理请求的对象集。

Command模式

参考文献

Command模式在对象中封装了请求,这样就可以保存命令,将该命令传递给方法以及像任何其他对象一样返回该命令。图7-14所示的是Command模式。

其优点如下。

● 将调用操作的对象与知道如何完成该操作的对象相分离。

● 更容易添加新命令,因为不用修改已有类。

在以下情况中,应该使用Command模式:

● 想要通过要执行的动作来参数化对象。

● 要在不同的时间指定、排序以及执行请求。

● 必须支持Undo、日志记录或事务。

图7-14 Command模式找不到图片(Image not found)
Interpreter模式

解释器模式

interpreter模式可以解释定义其语法表示的语言,还提供了用表示来解释语言中的语句的解释器。图7-1所示的是Interpreter模式。

图7-15 Interpreter模式找不到图片(Image not found)

其优点如下。

● 容易修改并扩展语法。

● 更容易实现语法。

在以下情况中,应该使用Interpreter模式:

● 语言的语法比较简单。

● 效率并不是最主要的问题。

Iterator模式

Iterator模式为集合中的有序访问提供了一致的方法,而该集合是独立于基础集合,并与之相分离的。图7-16所示的是Iterator模式。

图7-16 Iterator模式找不到图片(Image not found)

其优点如下。

● 支持集合的不同遍历。

● 简化了集合的接口。

在以下情况中,应该使用Iterator模式:

● 在不开放集合对象内部表示的前提下,访问集合对象内容。

● 支持集合对象的多重遍历。

● 为遍历集合中的不同结构提供了统一的接口。

Mediator模式

Mediator模式通过引入一个能够管理对象间消息分布的对象,简化了系统中对象间的通信。该模式可以减少对象之间的相互引用,从而提高了对象间的松耦合度,并且它还可以独立地改变其间的交互。图7-17所示的是Mediator模式。

其优点如下。

● 去除对象间的影响。

● 简化了对象间协议。

● 集中化了控制。

● 由于不再需要直接互传消息,单个组件变得更加简单,而且容易处理。

● 由于不再需要包含逻辑来处理组件间的通信,组件变得更加通用。

在以下情况中,应该使用Mediator模式:

● 对象集合需要以一个定义规范但复杂的方式进行通信。

● 想要在不使用子类的情况下自定义分布在几个对象之间的行为。

图7-17 Mediator模式找不到图片(Image not found)
Memento模式

Memento模式可以保持对象状态的“快照”(snapshot),这样对象可以在不向外界公开其内容的情况下返回到它的最初状态。图7-18所示的是Memento模式。

图片详情找不到图片(Image not found)

其优点如下。

● 保持封装的完整。

● 简化了返回到初始状态所需的操作。

在以下情况中,应该使用Memento模式:

● 必须保存对象状态的快照,这样以后就可以恢复状态。

● 使用直接接口来获得状态可能会公开对象的实现细节,从而破坏对象的封装性。

Observer模式

Observer模式为组件向相关接收方广播消息提供了灵活的方法。该模式定义了对象间一到多的依赖关系,这样当对象改变状态时,将自动通知并更新它所有的依赖对象。图7-19所示的是Observer模式。

其优点如下。

● 抽象了主体与Observer之间的耦合关系。

● 支持广播方式的通信。

图7-19 Observer模式找不到图片(Image not found)

在以下情况中,应该使用Observer模式:

● 对一个对象的修改涉及对其他对象的修改,而且不知道有多少对象需要进行相应修改。

● 对象应该能够在不用假设对象标识的前提下通知其他对象。

State模式

State模式允许对象在内部状态变化时,变更其行为,并且修改其类。图7-20所示的是State模式。

图7-20 State模式找不到图片(Image not found)

其优点如下。

● 定位指定状态的行为,并且针对不同状态来划分行为,使状态转换显式进行。在以下情况中,应该使用State模式:

● 对象的行为依赖于其状态,并且该对象必须在运行时根据其状态修改其行为。

● 操作具有大量以及多部分组成的取决于对象状态的条件语句。

Strategy模式

Strategy模式定义了一组能够用来表示可能行为集合的类。这些行为可以在应用程序中使用,来修改应用程序功能。图7-21所示的是Strategy模式。

图7-21 Strategy模式找不到图片(Image not found)

其优点如下。

● 另一种子类化方法。

● 在类自身中定义了每一个行为,这样就减少了条件语句。(减少条件语句的参考文献)

● 更容易扩展模型。在不对应用程序进行代码修改的情况下,使该模式具有新的行为。

在以下情况中,应该使用Strategy模式:

● 许多相关类只是在行为方面有所区别。

● 需要算法的不同变体。

● 算法使用客户端未知的数据。

Template Method模式

Template Method模式提供了在不重写方法的前提下允许子类重载部分方法的方法。在操作中定义算法的框架,将一些步骤由子类实现。该模式可以在不修改算法结构的情况下,让子类重新定义算法的特定步骤。图7-22所示的是Template Method模式。

图7-22 Template Method模式找不到图片(Image not found)

其优点为:代码重用的基础技术。

在以下情况中,应该使用Template Method模式:

● 想要一次实现算法的不变部分,而使用子类实现算法的可变行为。

● 当子类间的通用行为需要分解、定位到通用类的时候,这样可以避免代码重复的问题。

Visitor模式

Visitor模式提供了一种方便的、可维护的方法来表示在对象结构元素上要进行的操作。该模式允许在不改变操作元素的类的前提下定义一个新操作。图7-23所示的是Visitor模式。

图7-23 Visitor模式找不到图片(Image not found)

其优点如下。

● 更容易添加新操作。

● 集中相关操作并且排除不相关操作。

在以下情况中,应该使用Visitor模式:

● 对象结构包含许多具有不同接口的对象类,并且想要对这些依赖于具体类的对象进行操作。

● 定义对象结构的类很少被修改,但想要在此结构之上定义新的操作。

网课

设计原则

  • 单一职责原则:设计目的单一的类
  • 开放-封闭原则:对扩展开放,对修改封闭
  • 李氏(Liskov)替换原则:子类可以替换父类,(不要随便重载)
  • 依赖倒置原则︰要依赖于抽象,而不是具体实现;针对接口编程,不要针对实现编程(电视机:针对实现,计算机:针对接口)
  • 接口隔离原则:使用多个专门的接口比使用单一的总接口要好
  • 组合重用原则∶要尽量使用组合,而不是继承关系达到重用目的
  • 迪米特(Demeter)原则(最少知识法则):一个对象应当对其他对象有尽可能少的了解(不设为私有的就会直接去使用内部的内容)

面向对象设计(设计模式的概念)

  • √架构模式:软件设计中的高层决策,例如C/S结构就属于架构模式,架构模式反映了开发软件系统过程中所作的基本设计决策
  • √设计模式:主要关注软件系统的设计,与具体的实现语言无关
  • √惯用法:是最低层的模式,关注软件系统的设计与实现,实现时通过某种特定的程序设计语言来描述构件与构件之间的关系。每种编程语言都有它自己特定的模式,即语言的惯用法。例如引用-计数就是C++语言中的一种惯用法

面向对象设计(设计模式的分类)

图片详情找不到图片(Image not found)

行为:类之间交互,职责的方面
结构:类和对象之间的组合

创建型模式

创建型模式找不到图片(Image not found)

结构型模式

图片详情找不到图片(Image not found)

行为型模式

图片详情找不到图片(Image not found)
图片详情找不到图片(Image not found)