七、面向对象技术

1 面向对象基础

面向对象方法的特点

  • 以对象为中心:符合人类思维方式,易于理解和接受。
  • 稳定性:客观世界中的对象及其关系相对稳定,适应需求变化。
  • 统一建模语言(UML):标准化建模语言,统一了面向对象方法的术语、概念和模型,成为工业标准。

面向对象方法的阶段

  • 面向对象分析:从需求出发识别对象和类。
  • 面向对象设计:设计类的结构和对象间的交互。
  • 面向对象实现:用面向对象语言实现设计。

1.1 面向对象的基本概念

  1. 对象 - 对象是基本的运行实体,包含属性(数据)和行为(操作)。对象通过封装将属性和行为结合为一个整体,隐藏内部实现细节。
  2. 消息 - 对象间通过消息通信,消息包含要求接收对象执行活动的信息。发送消息的对象无需了解接收对象的响应细节。
  3. 类 - 类定义了一组相似对象的共同属性和行为。对象是类的实例。类可以分为实体类、接口类和控制类。
  4. 继承 - 继承是类之间共享数据和方法的机制。子类继承父类的属性和方法,并可扩展或覆盖这些特性。继承分为单重继承和多重继承。
  5. 多态 - 多态指不同对象对同一消息产生不同响应的现象。多态利用继承层次结构,将通用功能放在高层,具体实现放在低层。过载(Ovrerloading)多态是同一个名字在不同的上下文所代表的含义不同。
  6. 动态绑定 - 动态绑定在运行时将过程调用和代码绑定,支持多态。父类对象可以引用子类对象,调用时根据实际对象类型执行相应方法。

1.2 面向对象分析

面向对象分析(Object-Oriented Analysis,OOA)的目的是为了获得对应用问题的理解。理解的目的是确定系统的功能、性能要求。面向对象分析技术可以将系统的行为和信息间的关系表示为迭代构造特征。面向对象分析包含 5 个活动:认定对象、组织对象、描述对象间的相互作用、确定对象的操作、定义对象的内部信息。

  1. 认定对象
  • 方法:在应用领域中,通过自然存在的实体确立对象,通常从名词开始。
  • 目的:识别系统关心的实质性对象,奠定系统稳定性基础。
  1. 组织对象
  • 方法:分析对象间关系,将相关对象抽象成类,建立继承性类结构。
  • 目的:简化关联对象,通过继承构造类层次。
  1. 对象间的相互作用
  • 描述:明确对象间的关系,如组成关系和通信关系。
  • 目的:完整描述每个对象的环境和交互方式。
  1. 基于对象的操作
  • 操作类型:包括简单操作(创建、增加、删除)和复杂操作。
  • 目的:定义对象的操作后,进一步明确对象内部定义和属性。

1.3 面向对象设计

面向对象设计(Object-Oriented Design,OOD)是将 OOA 所创建的分析模型转化为设计模型,其目标是定义系统构造蓝图。

1.3.1 面向对象设计的活动

  1. 识别类及 OOA 模型
  2. 定义属性和对象
  3. 定义服务
  4. 识别关系
  5. 识别包

1.3.2 面向对象设计的原则

1.3.2.1 面向对象方法的五大原则

面向对象方法的五大原则可以用“SOLID”来记忆,具体如下:

  1. Single Responsibility Principle (SRP) - 单一责任原则- 每个类应只有一个引起变化的原因。
  2. Open/Closed Principle (OCP) - 开放-封闭原则- 软件实体应可扩展但不可修改。
  3. Liskov Substitution Principle (LSP) - 里氏替换原则- 子类必须能够替换其基类。
  4. Interface Segregation Principle (ISP) - 接口分离原则- 不应强迫客户依赖于它们不用的方法。
  5. Dependency Inversion Principle (DIP) - 依赖倒置原则- 高层模块不应依赖低层模块,两者应依赖于抽象。
1.3.2.2 其他设计原则
  1. 重用发布等价原则(REP)- 重用的粒度即发布的粒度。
  2. 共同封闭原则(CCP)- 包中的类应对同一类型的变化共同封闭。
  3. 共同重用原则(CRP)- 如果重用了一个类,则需重用整个包。
  4. 无循环依赖原则(ADP)- 包的依赖关系中不能有环。
  5. 稳定依赖原则(SDP) - 依赖于稳定的方向。
  6. 稳定抽象原则(SAP) - 包的抽象程度应与其稳定程度一致。

1.4 面向对象程序设计

程序设计范型(Programming Paradigm)是人们在程序设计时所采用的基本方式模型。面向对象程序设计(Object-Oriented Programming,OOP)的实质是选用一种面向对象程序设计语言(Object-Oriented Programming Language,OOPL),采用对象、类及其相关概念所进行的程序设计。它的关键在于加入了类和继承性,从而进一步提高了抽象程度。

1.4.1 类

  • 定义:类是对象的模板,定义了一组具有相同属性和行为的对象。
  • 特征
    1. 实例化:支持实例生成(构造函数)和实例消除(析构函数)。
    2. 数据结构和行为一致性:同一类的不同实例具有相同数据结构和行为。
    3. 状态差异:不同实例可以有不同的状态。
    4. 初始状态设定:实例的初始状态可以在实例化时确定。

1.4.2 继承和类层次结构

  • 定义:继承允许子类继承父类的属性和方法。
  • 类层次结构:通过继承关系形成树状或网状结构。

1.4.3 对象、消息传递和方法

  • 对象通信:对象通过消息传递进行通信。
  • 消息格式:包含对象名、方法名和参数。
  • 方法调用:对象接收消息并执行相应方法。

1.4.4 对象自身引用

  • 定义:对象自身引用(如 C++ 中的 this)指向当前对象。
  • 作用:在方法中访问对象的成员变量和方法。

1.4.5 重置(Overriding)

  • 定义:重置(重载、覆盖、重写、overriding)要求子类必须重新定义父类方法,通过动态绑定实现。
  • 实现:C++ 中用虚函数(Virtual Function),Java 中用抽象方法(Abstract Methond)。
  • 作用:允许子类根据自身需求替换父类方法实现。

1.4.6 类属类(Template Class)

  • 定义:类的模板,强调与具体类型无关的特征。
  • 示例:C++ 中使用 template 关键字定义类属类。
  • 作用:支持参数多态,便于类库建设。

1.4.7 无实例的类(Abstract Class)

  • 定义:不能创建实例的类,包含抽象方法。
  • C++ 实现:包含纯虚函数的类。
  • Java 实现:用 abstract 关键字声明的抽象类。
  • 作用:提供通用接口,子类必须实现抽象方法。

1.5 面向对象测试

面向对象测试的层次

  1. 类层:测试类中定义的每个方法,相当于传统测试中的单元测试
  2. 层:测试同一类中所有方法与属性的相互作用,属于面向对象测试特有的模块测试。
  3. 模板层:测试协同工作的类之间的相互作用,相当于传统测试中的集成测试,但包含面向对象的特点,如对象间通过消息通信。
  4. 系统层:将各个子系统组装成完整的面向对象软件系统,并在组装过程中进行测试。

测试策略

  • 综合测试:由底向上进行,逐步连接对象,早期阶段处理通信问题。
  • 目标:以较低的测试成本和较少的测试用例发现尽可能多的错误。

面向对象测试的特点

  • 封装、继承和多态:增加了测试和调试的难度,需要特别关注这些机制的影响。

2 UML

面向对象分析强调的是对一个系统中对象的特征和行为的定义。目前,国际上已经出现了
多种面向对象的方法,例如 UML(Unified Modeling Language,统一建模语言是一种标准化的方法,用于可视化复杂系统(如软件体系结构或数据库),并使组件的关系、特征和行为易于理解。

2.1 事物

UML 包含 4 种事物:

  1. 结构事物
    • 类:表示具有相同属性和方法的对象集合。
    • 接口:定义一组操作规范。
    • 协作:描述多个对象间的交互。
    • 用例:用户与系统交互的功能单元。
    • 主动类:具有独立控制线程的类。
    • 构件:可部署的物理单元。
    • 制品:系统中的物理文件。
    • 结点:计算资源的表示。
  2. 行为事物
    • 交互:对象间的通信,包括消息和动作序列。
    • 状态机:描述对象生命周期内的状态变化。
    • 活动:跨时间和空间的行为描述。
  3. 分组事物
    • 包:组织和管理模型元素的机制。
  4. 注释事物
    • 注解:对模型元素的描述和解释。

2.2 关系

UML 包含 4 种关系:

  1. 依赖 Dependency
    • 语义关系,一个事物的变化影响另一个事物。
    • 虚线箭头表示。
  2. 关联 Association
    • 结构关系,描述对象间的连接。**聚集(聚合、Aggregation)**关系是一种特殊的关联,是一种弱的“拥有”关系。
    • 包括聚合(整体与部分关系)。
    • 可标注多重度和角色。
  3. 泛化 Generalization
    • 特殊/一般关系,子类继承父类。
    • 带空心箭头的实线表示,箭头指向父类。
  4. 实现 Realization
    • 类元间的契约关系。
    • 接口和实现类之间,或用例和协作之间。
    • 带空心箭头的虚线表示。

2.3 UML 中的图

UML 图表有两种主要类型:结构图和行为图。

2.3.1 结构图

Structure Diagram(结构图)显示了系统的静态结构,包括其属性和实现级别。

2.3.1.1 类图
  • 内容:类图(Class)展示对象、接口、协作及其关系。
  • 用途:系统静态设计视图,支持功能需求建模。
  • 应用场景
    • 系统词汇建模。
    • 简单协作建模。
    • 逻辑数据库模式建模。
2.3.1.2 对象图
  • 内容:对象图(Object)展示特定时刻的对象及其关系。
  • 用途:静态设计视图或静态进程视图,支持功能需求建模。
  • 应用场景:对象结构建模。
2.3.1.3 构件图
  • 定义:构件图(组件图、Component Diagram)展示构件及其依赖关系。
  • 内容:构件及其接口和依赖关系。
  • 用途:系统的静态实现视图。
2.3.1.4 组合结构图
  • 定义:组合结构图(复合结构图、Composite Structure Diagram)描述分类器的内部结构和协作关系。是一种较不常用的图样式。
  • 内容:分类器的内部结构和与其他部分的交互。
  • 用途:展示设计架构或模式。
2.3.1.5 部署图
  • 定义:部署图(Deployment)对面向对象系统的物理方面进行建模,展示运行时处理结点及构件配置。
  • 内容:结点及其包含的构件(制品)。
  • 用途:系统的静态部署视图。
2.3.1.6 包图
  • 定义:包图(Package)组织模型元素为层次结构,展示包及其依赖关系。
  • 内容:包内包含的元素(类、接口、构件等)及包间依赖。
  • 用途:管理模型元素,形成命名空间。使得程序员能够展示大型组件组之间的关系。
2.3.1.7 外廓图【拓展、不考】

外廓(Profile)图是基于 UML 元素的子集为特定领域定义 UML 的一个特定版本,即定义了一组对 UML 已有模型的扩展和限定机制,以用于某个特定领域,自 UML 2.3 起,UML标准新增了外廓图。即如果目前所有的 UML 图如果满足不了业务建模诉求的时候,就可以用外廓图在已有的模型上扩展或减少一些 UML 元模型元素,“创造”出一种新的建模图。

2.3.2 行为图

行为图(Behavior)显示了系统的动态行为,例如它可能随时间更改的方式。

2.3.2.1 用例图
  • 内容:展示用例、参与者及其关系。
  • 用途:系统静态用例视图,支持系统行为建模。
  • 应用场景
    • 系统语境建模。
    • 系统需求建模。
2.3.2.2 交互图
2.3.2.2.1 序列图
  • 定义:序列图(Sequence)强调消息的时间顺序。
  • 特征
    • 对象生命线:表示对象存在的时间段。
    • 控制焦点:表示对象执行动作的时间段。
  • 用途:可视化对象间的交互时序。
2.3.2.2.2 通信图
  • 定义:Communication Diagram 强调收发消息的对象的结构组织。
  • 特征
    • 路径:指示对象间的链接路径。
    • 顺序号:表示消息的时间顺序。
    • 用途:展示对象间的结构化交互。
2.3.2.2.3 交互概览图
  • 定义:UML 2.0 新增 Interaction Overview Diagram,描述业务过程的控制流概览。是活动图的变体。
  • 特征:使用活动图表示法,抽象掉消息和生命线。
  • 用途:概述业务流程中的交互。
2.3.2.2.4 计时图
  • *定义:计时图(时间图、Timing Diagram)关注线性时间轴上的条件变化。
  • *特征:描述对象状态随时间的变化。
  • *用途:适合分析周期性和非周期性任务。
2.3.2.3 状态图
  • 定义:State Diagram 展示状态机,由状态、转换、事件和活动组成。
  • 组成:
    • 状态:对象生命周期中的条件或活动。
    • 转换:由事件触发的状态变化。
    • 事件:引发状态变化的触发条件。
    • 活动:状态中的执行动作。
  • 用途:系统的动态视图,特别适用于接口、类和协作的行为建模。
2.3.2.4 活动图
  • 定义:Activity Diagram 特殊的动态图,展示系统内活动流程。描述了一个开始和结束均清晰的逐步过程。
  • 组成
    • 活动状态:系统的活动步骤。
    • 动作状态:不可分解的原子操作。
    • 分支和合并:基于条件的路径选择。
    • 分叉和汇合:并发控制流的表示。
  • 用途:系统的动态视图,强调功能建模和控制流程。

3 设计模式

3.1 设计模式的要素

设计模式通常包含以下四个基本要素:

  1. 模式名称(Pattern Name):用一两个词描述模式问题、解决方案和效果,便于交流和文档编写。
  2. 问题(Problem):描述模式适用的场景,包括设计问题及其背景。
  3. 解决方案(Solution):描述设计的组成部分、相互关系及职责,提供抽象解决方案而非具体实现。
  4. 效果(Consequences):描述模式的应用效果及权衡问题,包括对系统灵活性、可扩展性和可移植性的影响。

3.2 设计模式的分类

设计模式分为三大类:

  1. 创建型模式:与对象的创建有关。
    • Factory Method(类)
    • Abstract Factory(对象)
    • Builder(对象)
    • Prototype(对象)
    • Singleton(对象)
  2. 结构型模式:处理类或对象的组合。
    • Adapter(类)
    • Adapter(对象)
    • Bridge(对象)
    • Composite(对象)
    • Decorator(对象)
    • Facade(对象)
    • Flyweight(对象)
    • Proxy(对象)
  3. 行为型模式:描述类或对象的交互和职责分配。
    • Interpreter(类)
    • Template Method(类)
    • Chain of Responsibility(对象)
    • Command(对象)
    • Iterator(对象)
    • Mediator(对象)
    • Memento(对象)
    • Observer(对象)
    • State(对象)
    • Strategy(对象)
    • Visitor(对象)

记忆口诀:

  • 结构型 (共 7 种) :四桥组装外箱带 adapter 适配器、bridge 桥接、composite 组合、decorator 装饰、facade 外观、flyweight 享元、proxy 代理模式;
  • 类类型设计模式 只有 4 种:公司模姐 工厂、适配器、模板、解释器。特殊:适配器既属于类有属于对象类型设计模式。

3.3 创建型设计模式

3.3.1 Abstract Factory(抽象工厂)

  • 意图
    • 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
  • 结构
    • AbstractFactory:声明创建抽象产品对象的接口。
    • ConcreteFactory:实现创建具体产品对象的操作。
    • AbstractProduct:为一类产品对象声明接口。
    • ConcreteProduct:定义具体产品对象,实现 AbstractProduct 接口。
    • Client:使用 AbstractFactory 和 AbstractProduct 接口。
  • 适用于
    • 系统要独立于产品的创建、组合和表示。
    • 系统由多个产品系列中的一个配置。
    • 提供一个产品类库,只显示接口而非实现。

3.3.2 Builder(生成器)

  • 意图
    • 将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  • 结构
    • Builder:为创建Product对象的各个部件指定抽象接口。
    • ConcreteBuilder:实现 Builder 接口,构造和装配产品部件。
    • Director:构造一个使用 Builder 接口的对象。
    • Product:表示被构造的复杂对象。
  • 适用于
    • 创建复杂对象的算法独立于组成部分及其装配方式。
    • 构造过程允许不同的表示。

3.3.3 Factory Method(工厂方法)

  • 意图
    • 定义创建对象的接口,让子类决定实例化哪一个类。实例化延迟到子类。
  • 结构
    • Product:定义工厂方法创建的对象接口。
    • ConcreteProduct:实现 Product 接口。
    • Creator:声明工厂方法,返回 Product 对象。
    • ConcreteCreator:重定义工厂方法,返回 ConcreteProduct 实例。
  • 适用于
    • 类不知道所创建对象的具体类。
    • 希望子类指定创建的对象。
    • 将创建职责委托给多个子类。

3.3.4 Prototype(原型)

  • 意图
    • 用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
  • 结构
    • Prototype:声明复制自身的接口。
    • ConcretePrototype:实现复制自身的操作。
    • Client:通过原型复制创建新对象。
  • 适用于
    • 系统独立于产品创建、构成和表示。
    • 运行时刻指定产品类,如动态装载。
    • 避免构建与产品类层次平行的工厂类。
    • 类实例仅有几种状态组合时,克隆原型更方便。

3.3.5 Singleton(单例)

  • 意图
    • 保证一个类仅有一个实例,并提供全局访问点。
  • 结构
    • Singleton:通过 Instance() 提供唯一实例访问。
  • 适用于
    • 类只有一个实例且需全局访问。
    • 唯一实例需可扩展,客户端无需改动即可使用扩展实例。

3.4 结构型设计模式

3.4.1 Adapter(适配器)

  • 意图
    • 将一个类的接口转换成客户希望的另一个接口,使原本因接口不兼容而无法一起工作的类可以协同工作。
  • 结构
    • Target:定义客户使用的接口。
    • Client:与符合 Target 接口的对象协作。
    • Adapter:适配 Adaptee 的接口与 Target 接口。
    • Adaptee:定义需要适配的现有接口。
    • 类适配器:通过多重继承匹配接口。
    • 对象适配器:依赖对象组合进行适配。
  • 适用于
    • 想使用现有类,但接口不符合要求。
    • 创建可与其他不相关类协同工作的类。
    • 对象适配器可适配已有子类的接口。

3.4.2 Bridge(桥接)

  • 意图
    • 将抽象部分与实现部分分离,使它们可以独立变化。
  • 结构
    • Abstraction:定义抽象类接口,维护指向 Implementor 的指针。
    • RefinedAbstraction:扩展 Abstraction 定义的接口。
    • Implementor:定义实现类的接口。
    • ConcreteImplementor:实现 Implementor 接口。
  • 适用于
    • 抽象和实现不希望有固定绑定关系。
    • 抽象和实现需独立扩展。
    • 修改实现对客户透明。
    • 多个对象共享实现,客户无需知晓。

3.4.3 Composite(组合)

  • 意图
    • 将对象组合成树形结构表示“部分-整体”的层次结构,使用户可以一致地使用单个对象和组合对象。
  • 结构
    • Component:声明组合中对象的接口。
    • Leaf:表示叶节点对象,无子节点。
    • Composite:定义有子对象的组件行为,存储子组件。
    • Client:通过 Component 接口操作组合对象。
  • 适用于
    • 表示对象的“部分-整体”层次结构。
    • 用户需统一使用组合结构中的所有对象。

3.4.4 Decorator(装饰)

  • 意图
    • 动态地给对象添加一些额外的职责,比生成子类更加灵活。
  • 结构
    • Component:定义对象接口。
    • ConcreteComponent:具体对象,可以添加职责。
    • Decorator:维持指向 Component 对象的指针,定义一致接口。
    • ConcreteDecorator:向组件添加职责。
  • 适用于
    • 动态透明地给单个对象添加职责。
    • 处理可撤销的职责。
    • 当不能使用子类扩展时。

3.4.5 Facade(外观)

  • 意图
    • 为子系统提供一个一致的简单接口。
  • 结构
    • Facade:定义高层接口,代理子系统对象。
    • Subsystem Classes:实现子系统功能。
  • 适用于
    • 简化复杂子系统的使用。
    • 提高子系统的独立性和可移植性。

3.4.6 Flyweight(享元)

  • 意图
    • 用共享技术支持大量细粒度对象。
  • 结构
    • Flyweight:定义接口,接受并作用于外部状态。
    • ConcreteFlyweight:实现接口,存储内部状态。
    • UnsharedConcreteFlyweight:非共享具体享元。
    • FlyweightFactory:创建和管理享元对象。
  • 适用于
    • 应用程序使用大量对象导致存储开销大。
    • 对象状态大多可变为外部状态。
    • 需要共享对象以减少存储需求。

3.4.7 Proxy(代理)

  • 意图
    • 为其他对象提供代理以控制访问。
  • 结构
    • Subject:定义共享接口。
    • RealSubject:实际对象。
    • Proxy:保存引用,控制对 RealSubject 的访问。
  • 适用于
    • 远程代理:为不同地址空间的对象提供局部代表。
    • 虚拟代理:延迟创建开销大的对象。
    • 保护代理:控制对象访问权限。
    • 智能引用:执行附加操作(如计数、检查)。

3.5 行为型设计模式

3.5.1 Chain of Responsibility(责任链)

  • 意图
    • 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
  • 结构
    • Handler:定义一个处理请求的接口;(可选)实现后继链。
    • ConcreteHandler:处理它所负责的请求;可访问它的后继者;如果可处理该请求,就处理它,否则将该请求转发给后继者。
    • Client:向链上的具体处理器(ConcreteHandler)对象提交请求。

3.5.2 Command(命令)

  • 意图
    • 将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
  • 结构
    • Command:声明执行操作的接口。
    • ConcreteCommand:将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现 Execute。
    • Invoker:要求该命令执行这个请求。
    • Receiver:知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。

3.5.3 Interpreter(解释器)

  • 意图
    • 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
  • 结构
    • AbstractExpression:声明一个解释操作的接口,这个接口为抽象语法树中所有的结点所共享。
    • TerminalExpression:实现与文法中的终结符相关联的解释操作;一个句子中的每个终结符需要该类的一个实例。
    • NonterminalExpression:对文法中的每一条规则都需要一个 NonterminalExpression 类;为每个符号都维护一个 AbstractExpression 类型的实例变量;为文法中的非终结符实现解释操作。
    • Context:包含解释器之外的一些全局信息。
    • Client:构建(或给定)表示该文法定义的语言中一个特定的句子的抽象语法树;该抽象语法树由 NonterminalExpression 和 TerminalExpression 的实例装配而成;调用解释操作。

3.5.4 Iterator(迭代器)

  • 意图
    • 提供一种方法顺序访问一个聚合对象中的各个元素,且不需要暴露该对象的内部表示。
  • 结构
    • Iterator(迭代器):定义访问和遍历元素的接口。
    • ConcreteIterator(具体迭代器):实现迭代器接口;对该聚合遍历时跟踪当前位置。
    • Aggregate(聚合):定义创建相应迭代器对象的接口。
    • ConcreteAggregate(具体聚合):实现创建相应迭代器的接口,该操作返回 ConcreteIterator 的一个适当的实例。
  • 适用于
    • 访问一个聚合对象的内容而无须暴露它的内部表示。
    • 支持对聚合对象的多种遍历。
    • 为遍历不同的聚合结构提供一个统一的接口。

3.5.5 Mediator(中介者)

  • 意图
    • 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  • 结构
    • Mediator(中介者):定义一个接口用于各同事(Colleague)对象通信。
    • ConcreteMediator(具体中介者):通过协调各同事对象实现协作行为;了解并维护它的各个同事。
    • Colleague class(同事类):知道它的中介者对象;每一个同事类对象在需要与其他同事通信的时候与它的中介者通信。
  • 适用于
    • 一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。
    • 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
    • 想定制一个分布在多个类中的行为,而又不想生成太多的子类。

3.5.6 Memento(备忘录)

  • 意图
    • 在不破坏封装性的前提下捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
  • 结构
    • Memento(备忘录):存储原发器对象的内部状态,原发器根据需要决定备忘录存储原发器的哪些内部状态;防止原发器以外的其他对象访问备忘录。
    • Originator(原发器):创建一个备忘录,用于记录当前时刻它的内部状态;使用备忘录恢复内部状态。
    • Caretaker(管理者):负责保存好备忘录;不能对备忘录的内容进行操作或检查。
  • 适用于
    • 必须保存一个对象在某一个时刻的部分状态,这样以后需要时它才能恢复到先前的状态。
    • 如果一个用接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

3.5.7 Observer(观察者)

  • 意图
    • 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时所有依赖于它的对象都得到通知并被自动更新。
  • 结构
    • Subject(目标):知道它的观察者,可以有任意多个观察者观察同一个目标;提供注册和删除观察者对象的接口。
    • Observer(观察者):为那些在目标发生改变时需获得通知的对象定义一个更新接口。
    • ConcreteSubject(具体目标):将有关状态存入各 ConcreteObserver 对象;当它的状态发生改变时,向它的各个观察者发出通知。
    • ConcreteObserver(具体观察者):维护一个指向 ConcreteSubject 对象的引用;存储有关状态,这些状态应与目标的状态保持一致;实现 Observer 的更新接口,以使自身状态与目标的状态保持一致。
  • 适用于
    • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。
    • 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时。
    • 当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的。

3.5.8 State(状态)

  • 意图
    • 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
  • 结构
    • Context(上下文):定义客户感兴趣的接口;维护一个 ConcreteState 子类的实例,这个实例定义当前状态。
    • State(状态):定义一个接口以封装与 Context 的一个特定状态相关的行为。
    • ConcreteState(具体状态子类):每个子类实现与 Context 的一个状态相关的行为。
  • 适用于
    • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
    • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State 模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。

3.5.9 Strategy(策略)

  • 意图
    • 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。此模式使得算法可以独立于使用它们的客户而变化。
  • 结构
    • Strategy(策略):定义所有支持的算法的公共接口。Context 使用这个接口来调用某 ConcreteStrategy 定义的算法。
    • ConcreteStrategy(具体策略):以 Strategy 接口实现某具体算法。
    • Context(上下文):用一个 ConcreteStrategy 对象来配置;维护一个对 Strategy 对象的引用;可定义一个接口来让 Strategy 访问它的数据。
  • 适用于
    • 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
    • 需要使用一个算法的不同变体。例如,定义一些反映不同空间的时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
    • 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
    • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的 Strategy 类中,以代替这些条件语句。

3.5.10 Template Method(模板方法)

  • 意图
    • 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的骨架即可重定义该算法的某些特定步骤。
  • 结构
    • AbstractClass(抽象类):定义抽象的原始操作,具体的子类将重定义它们以实现一个算法的各步骤;实现模板方法,定义一个算法的骨架,该模板方法不仅调用原始操作,也调用定义在 AbstractClass 或其他对象中的操作。
    • ConcreteClass(具体类):实现原始操作以完成算法中与特定子类相关的步骤。
  • 适用于
    • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
    • 各子类中公共的行为应被提取出来并集中到一个公共父类中,以避免代码重复。
    • 控制子类扩展。模板方法在特定位点调用“hook”操作(默认的行为,子类可以在必要时进行重定义扩展),这就只允许在这些点进行扩展。

3.5.11 Visitor(访问者)

  • 意图
    • 表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的新操作。
  • 结构
    • Visitor(访问者):为该对象结构中 ConcreteElement 的每一个类声明一个 Visit 操作。该操作的名字和特征标识了发送 Visit 请求给该访问者的那个类,这使得访问者可以确定正被访问元素的具体的类。这样访问者就可以通过该元素的特定接口直接访问它。
    • ConcreteVisitor(具体访问者):实现每个有 Visitor 声明的操作,每个操作实现本算法的一部分,而该算法片段乃是对对应于结构中对象的类。ConcreteVisitor 为该算法提供了上下文并存储它的局部状态。这一状态常常在遍历该结构的过程中累积结果。
    • Element(元素):定义以一个访问者为参数的 Accept 操作。
    • ConcreteElement(具体元素):实现以一个访问者为参数的 Accept 操作。
    • ObjectStructure(对象结构):能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个组合或者一个集合,如一个列表或一个无序集合。
  • 适用于
    • 一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作。
    • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些操作“污染”这些对象的类。Visitor 使得用户可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。
    • 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。

4 加餐和总结

UML 是重点

4.1 类图-详解

类属性定义

  • + public
  • # protected
  • ~ package/default
  • - private

关系

  • 依赖(Dependency)用虚线箭头表示。
  • 关联(Association)用实线表示,关联可以有方向。默认是没方向的,称为双向关联。有方向的称为单向关联
    • 聚合(Aggregation)has a 或是 whole/part
    • 组合(Composition)是聚合的一个特例,所属关系更强
  • 继承/泛化(INheritance/Generalization)用带空心箭头的实线表示,箭头指向父类。
  • 实现(Realization)带空心箭头的虚线表示

关联关系的种类
• 按照关联所连接的类的数量,对象类之间的关联可分为:⾃返关联,⼆元关联,N元关联
• ⾃返关联(reflexive association)⼜称递归关联(recursive association),是⼀个类与本
⾝的关联,即同⼀个类的两个对象间的联系。
• ⼆元关联(binary association) :⼆元关联是在两个类之间的关联。
• N 元关联(n-ary association): 是在 3 个或 3 个以上类之间的关联。

关联关系的“多样性/维度”( Mul8plicity)
• 定义上页的关联关系时尝试回答以下问题:
• 市场活动(Campaign)类对象能否在没有员工(StaffMember)类对象管理的时候独立
存在?
• 如果可以,则员工(StaffMember)类的一端是可选的,多样性/维度定义为 0…*
• 如果不行,则员工(StaffMember)类是必选的,多样性/维度定义为 1…*
• 如果必须有,且只能由一个Staff来管理,多样性/维度定义为 1
• 然后再就另一端问同样的问题
• 每位员工都必须管理,且只能管理一项市场活动吗?
• 如果不是,则市场活动一端的多样性/维度定义为0…*

• 其他的多样性定义举例:
• 可选的(OpRonal,0 or 1) 0…1
• 有且仅有一个(Exactly one,1) 1…1
• 零或多个(Zero or more) 0…
• 一或多个(One or more) 1…*
• 取值范围(A range of values) 2…6