[读书笔记] 《大话设计模式》

.NET
397
0
0
2023-01-07

《大话设计模式》

作者:程杰

前言

成为 诗人 后 可能 不需要 刻意 地 按照 某种 模式 去 创作, 但 成为 诗人 前 他们 一定 是 认真 地 研究 过 成百上千 的 唐诗 宋词、 古今 名句。

第 1 章 代码无错就是优?—— 简单工厂模式

1.5 活字印刷,面向对象 > 位置 584

“第一, 要 改, 只需 更改 要 改之 字, 此为 可维护;第二, 这些 字 并非 用完 这次 就 无用, 完全可以 在后 来的 印刷 中 重复 使用, 此 乃 可复用;第三, 此 诗 若要 加 字, 只需 另 刻字 加入 即可, 这是 可扩展;第四, 字 的 排列 其实 可能 是 竖排 可能 是 横排, 此时 只需 将 活字 移动 就可 做到 满足 排列 需求, 此 是 灵活性好。”

1.10 简单工厂模式 > 位置 671

简单工厂模式, 也就是说, 到底 要 实例 化 谁, 将来 会不会 增加 实例 化 的 对象, 比如 增加 开 根 运算, 这是 很容易 变化 的 地方, 应该 考虑 用 一个 单独 的 类 来做 这个 创造 实例 的 过程, 这就 是 工厂(备注:先用父类声明对象,再看情况决定用哪个子类实例化对象)。

1.11 UML 类图 > 位置 689

UML 类 图 图示 样 例

img

注意 前面 的 符号,’+‘ 表示 public,’‘ 表示 private,’#‘ 表示 protected。

继承 关系 用 空心 三角形 + 实线 来 表示。

实现 接口 用 空心 三角形 + 虚线 来 表示。

当 一个 类’ 知道’ 另一个 类 时(备注:类里面有另一个类的对象), 可以 用 关联( association)。关联 关系 用 实线 箭头 来 表示。

聚合 表示 一种 弱 的’ 拥有’ 关系(备注:一个类中有另一个类的对象集合), 体现 的 是 A 对象 可以 包含 B 对象, 但 B 对象 不是 A 对象 的 一部分。聚合 关系 用 空心 的 菱形 + 实线 箭头 来 表示。”

合成( Composition, 也有 翻译 成’ 组合’ 的) 是一 种 强的’ 拥有’ 关系, 体现 了 严格 的 部分 和 整体 的 关系, 部分 和 整体 的 生命 周期 一样。(备注:整体类初始化时同时构造部分类的对象作为属性) 。合成 关系 用 实心 的 菱形 + 实线 箭头 来 表示。

另外, 你会 注意到 合成 关系 的 连线 两端 还有 一个 数字’ 1′ 和 数字’ 2’, 这 被称为 基数。表明 这 一端 的 类 可以 有几个 实例, 很 显然, 一个 鸟 应该 有两 只 翅膀。如果 一个 类 可能 有无 数个 实例, 则 就用’ n’ 来 表示。关联 关系、 聚合 关系 也可以 有 基数 的。

依赖 关系( Dependency)(备注:构造函数以依赖的对象作为参数), 用 虚线 箭头 来 表示。

第 2 章 商场促销 —— 策略模式

2.4 策略模式 > 位置 777

策略模式( Strategy)。策略 模式 定义 了 算法 家族, 分别 封装 起来, 让 它们 之间 可以 互相 替换, 此 模式 让 算法 的 变化, 不会 影响 到 使用 算法 的 客户。

img

2.7 策略模式解析 > 位置 821

策略 模式 是一 种 定义 一系列 算法 的 方法, 从 概念上 来看, 所有这些 算法 完成 的 都是 相同 的 工作, 只是 实现 不同, 它 可以 以 相同 的 方式 调用 所有 的 算法, 减少 了 各种 算法 类 与 使用 算法 类 之间 的 耦合 [DPE]。

另外 一个 策略 模式 的 优点 是 简化 了 单元 测试, 因为 每个 算法 都有 自己的 类, 可以 通过 自己的 接口 单独 测试 [DPE]。

策略 模式 封装了变化

当 不同 的 行为 堆砌 在 一个 类 中 时, 就很 难 避免 使用 条件 语句 来 选择 合适 的 行为。将 这些 行为 封装 在 一个 个 独立 的 Strategy 类 中, 可以 在使 用 这些 行 为的 类 中 消除 条件 语句 [DP]。

策略 模式 就是 用来 封装 算法 的, 但在 实践中, 我们 发现 可以 用 它来 封装 几乎 任何 类型 的 规则, 只要 在 分析 过程中 听到 需要 在不同时间应用不同的业务规则, 就可以 考虑 使用 策略 模式 处理 这种 变化 的 可能性 [DPE]”。

第 3 章 拍摄 UFO—— 单一职责原则

3.4 单一职责原则 > 位置 879

单一职责原则, 意思 就是说, 功能 要 单一?

哈, 可以 简单 地 这么 理解, 它的 准确 解释 是, 就 一个 类 而言, 应该 仅有 一个 引起 它 变化 的 原因 [ASD]。

3.5 方块游戏的设计 > 位置 903

如果 一个 类 承担 的 职责 过多, 就 等于 把这 些 职责 耦合 在一起, 一个 职责 的 变化 可能 会 削弱 或者 抑制 这个 类 完成 其他 职责 的 能力。这种 耦合 会 导致 脆弱 的 设计, 当 变化 发生 时, 设计 会 遭受 到 意想不到 的 破坏 [ASD]。

第 4 章 考研求职两不误 —— 开放 – 封闭原则

4.1 考研失败 > 位置 958

一个 国家, 两种制度, 这 在政治上, 是 伟大 的 发明 哦。在 软件 设计 模式 中, 这种 不能 修改, 但可以 扩展 的 思想 也是 最重要的 一种 设计 原则, 它 就是 开放 – 封闭 原则( The Open- Closeed Principle, 简称 OCP) 或 叫开 – 闭 原则。

4.2 开放 – 封闭原则 > 位置 962

这个 原则 其实 是有 两个 特征, 一个 是说’ 对于 扩展 是 开放 的( Open for extension)’, 另一个 是说’ 对于 更改 是 封闭 的( Closed for modification)'[ASD]。

4.3 何时应对变化 > 位置 994

在 我们 最初 编写 代码 时, 假设 变化 不会 发生。当 变化 发生 时, 我们 就 创建 抽象 来 隔离 以后 发生 的 同类 变化 [ASD]。

第 5 章 会修电脑不会修收音机?—— 依赖倒转原则

5.3 依赖倒转原则 > 位置 1083

依赖 倒转 原则, 原话 解释 是 抽象 不应该 依赖 细节, 细节 应该 依赖于 抽象, 这 话 绕口, 说白 了, 就是 要 针对 接口 编程, 不要 对 实现 编程。

5.4 里氏代换原则 > 位置 1105

子类 型 必须 能够 替换 掉 它们 的 父 类型 [ASD]。

由于 有 里氏代换原则, 才使 得 开放 – 封闭 成为 了 可能。

由于 子 类型 的 可替换 性 才使 得 使用 父 类 类型 的 模块 在 无需 修改 的 情况下 就可以 扩展。

再回 过头 来看 依赖倒转原则, 高层 模块 不应该 依赖 低层 模块, 两个 都应 该 依赖 抽象。

依赖 倒转 其实 就是 谁也 不要 依靠 谁, 除了 约定 的 接口。

5.5 修收音机 > 位置 1131

依赖 倒转 其实 可以 说是 面向对象设计 的 标志, 用 哪种 语言 来 编写 程序 不重要, 如果 编写 时 考虑 的 都是 如何 针对 抽象 编程 而 不是 针对 细节 编程, 即 程序 中 所有 的 依赖 关系 都是 终止 于 抽象 类 或者 接口, 那就 是 面向 对象 的 设计, 反之 那就 是 过程 化 的 设计 了 [ASD]。

第 6 章 穿什么有这么重要?—— 装饰模式

6.4 装饰模式 > 位置 1229

装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。[DP]

装饰 模式( Decorator) 结构图:

img

6.6 装饰模式总结 > 位置 1281

装饰模式 是 为 已有 功能 动态 地 添加 更多 功能 的 一种 方式。

把 每个 要 装饰 的 功能 放在 单独 的 类 中, 并 让 这个 类 包装 它 所要 装饰 的 对象, 因此, 当 需要 执行 特殊 行为 时, 客户 代码 就可以 在 运行时 根据 需要 有选择 地、 按 顺序 地 使用 装饰 功能 包装 对象 了 [DP]。

6.6 装饰模式总结 > 位置 1289

装饰 模式 的 优点 我总 结下 来就 是, 把 类 中的 装饰 功能 从 类 中 搬移 去除, 这样 可以 简化 原 有的 类。

更大 的 好处 就是 有效地 把 类 的 核心 职责 和 装饰 功能 区分 开了。而且 可以 去除 相关 类 中 重复 的 装饰 逻辑。

第 7 章 为别人做嫁衣 —— 代理模式

7.5 代理模式 > 位置 1378

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。[DP]

代理 模式( Proxy) 结构图

img

7.6 代理模式应用 > 位置 1394

代理 模式 都用 在 一些 什么 场合 呢?

一般来说 分为 几种, 第一, 远程代理, 也就是 为 一个 对象 在 不同 的 地址 空间 提供 局部 代表。这样 可以 隐藏 一个 对象 存在 于 不同 地址 空间 的 事实 [DP]。

WebService 在. NET 中的 应用 :

在 应用 程序 的 项目 中 加入 一个 Web 引用, 引用 一个 WebService, 此时 会在 项目 中 生成 一个 WebReference 的 文件夹 和 一些 文件, 其实 它们 就是 代理, 这就 使得 客户 端 程序 调用 代理 就可以 解决 远程 访问 的 问题。

第二 种 应用 是 虚拟代理, 是 根据 需要 创建 开销 很大 的 对象。通过 它来 存放 实例 化 需要 很长 时间 的 真实 对象 [DP]。这样 就可以 达到 性能 的 最优化, 比如说 你 打开 一个 很大 的 HTML 网页 时, 里面 可能 有很 多的 文字 和 图片, 但 你 还是 可以 很快 打开 它, 此时 你 所 看到 的 是 所有 的 文字, 但 图片 却是 一张 一张 地下 载 后才 能看 到。那些 未 打开 的 图片 框, 就是 通过 虚拟 代理 来 替代 了 真实 的 图片, 此时 代理 存储 了 真实 图片 的 路径 和 尺寸。

第三 种 应用 是 安全代理, 用来 控制 真实 对象 访问 时 的 权限 [DP]。一般 用于 对象 应该 有 不同 的 访问 权限 的 时候。

第 四种 是 智能指引, 是指 当 调用 真实 的 对象 时, 代理 处理 另外 一些 事 [DP]。如 计算 真实 对象 的 引用 次数, 这样 当 该 对象 没有 引用 时, 可以 自动 释放 它;或当 第一次 引用 一个 持久 对象 时, 将它 装入 内存;或在 访问 一个 实际 对象 前, 检查 是否 已经 锁定 它, 以 确保 其他 对象 不能 改变 它。它们 都是 通过 代理 在 访问 一个 对象 时 附加 一些 内务 处理。

代理 模式 其实 就是 在 访问 对象 时 引入 一定程度 的 间接性, 因为 这种 间接 性, 可以 附加 多种 用途。

第 8 章 雷锋依然在人间 —— 工厂方法模式

8.4 简单工厂 vs. 工厂方法 > 位置 1472

简单工厂 模式 的 最大 优点 在于 工厂 类 中 包含 了 必要 的 逻辑 判断, 根据 客户 端 的 选择 条件 动态 实例 化 相关 的 类, 对于 客户 端来 说, 去 除了 与 具体 产品 的 依赖。

工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。[DP]

工厂 方法 模式( Factory Method) 结构图

img

把工厂类抽象出一个接口, 这个 接口 只有 一个 方法, 就是 创建 抽象 产品 的 工厂 方法。然后, 所 有的 要 生产 具体 类 的 工厂, 就去 实现 这个 接口, 这样, 一个 简单 工厂 模式 的 工厂 类, 变成 了 一个 工厂 抽象 接口 和 多个 具体 生成 对象 的 工厂。

工厂方法 把 简单 工厂 的 内部 逻辑 判断 移到 了 客户 端 代码 来 进行。你 想要 加 功能, 本来 是 改 工厂 类 的, 而 现在 是 修改 客户 端!

8.5 雷锋工厂 > 位置 1530

工厂 方法 克服 了 简单 工厂 违背 开放 – 封闭 原则 的 缺点, 又 保持 了 封装 对象 创建 过程 的 优点

它们 都是 集中 封装 了 对象 的 创建, 使得 要 更换 对象 时, 不需 要做 大的 改动 就可 实现, 降低 了 客户 程序 与 产品 对象 的 耦合。工厂 方法 模式 是 简单 工厂 模式 的 进一步 抽象 和 推广。由于 使 用了 多 态 性, 工厂 方法 模式 保持 了 简单 工厂 模式 的 优点, 而且 克服 了 它的 缺点。但 缺点 是 由于 每 加 一个 产品, 就 需要 加 一个 产品 工厂 的 类, 增加 了 额外 的 开发 量。

第 9 章 简历复印 —— 原型模式

9.3 原型模式 > 位置 1598

原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。[DP]

原型 模式( Prototype) 结构图 :

img

原型 模式 其实 就是 从 一个 对象 再创 建 另外 一个 可 定制 的 对象, 而且 不需 知道 任何 创建 的 细节。

因为 克隆 实在 是 太 常用 了, 所以. NET 在 System 命名 空间 中 提供 了 ICloneable 接口, 其中 就是 唯一 的 一个 方法 Clone(), 这样 你就 只需 要 实现 这个 接口 就可以 完成 原型 模式 了。

9.5 浅复制与深复制 > 位置 1622

MemberwiseClone() 方法 是 这样, 如果 字段 是 值 类型 的, 则 对 该 字段 执行 逐 位 复制, 如果 字段 是 引用 类型, 则 复制 引用 但不 复制 引用 的 对象;因此, 原始 对象 及其 复本 引用 同一 对象。

9.5 浅复制与深复制 > 位置 1641

浅复制, 被 复制 对象 的 所有 变量 都 含有 与 原来 的 对象 相同 的 值, 而 所有 的 对其 他 对象 的 引用 都 仍然 指向 原来 的 对象。

深复制 把 引用 对象 的 变量 指向 复制 过 的 新 对象, 而 不是 原 有的 被 引用 的 对象。

深 复制 要 深入 到 多少 层, 需要 事先 就 考虑 好, 而且 要 当心 出现 循环 引用 的 问题, 需要 小心 处理, 这里 比较 复杂, 可以 慢慢 研究。

9.6 简历的深复制实现 > 位置 1656

比如说, 数据 集 对象 DataSet, 它 就有 Clone() 方法 和 Copy() 方法, Clone() 方法 用来 复制 DataSet 的 结构, 但不 复制 DataSet 的 数据, 实现 了 原型 模式 的 浅 复制。 Copy() 方法 不但 复制 结构, 也 复制 数据, 其实 就是 实现 了 原型 模式 的 深 复制。

第 10 章 考题抄错会做也白搭 —— 模板方法模式

10.3 提炼代码 > 位置 1749

当 我们 要 完成 在某 一 细节 层次 一致 的 一个 过程 或 一系列 步骤, 但 其 个别 步骤 在 更 详细 的 层次 上 的 实现 可能 不 同时, 我们 通常考虑用 模板方法模式 来 处理。

10.4 模板方法模式 > 位置 1763

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

模板 方法 模式( TemplateMethod) 结构图 :

img

模板 方法 模式 是 通过 把 不变 行为 搬 移到 超 类, 去除 子类 中的 重复 代码 来 体现 它的 优势

当 不 变的 和 可变 的 行为 在 方法 的 子类 实现 中 混合 在一起 的 时候, 不变 的 行为 就会 在 子类 中 重复 出现。我们 通过 模板 方法 模式 把这 些 行为 搬移 到 单一 的 地方, 这样 就 帮助 子类 摆脱 重复 的 不变 行为 的 纠缠。

第 11 章 无熟人难办事?—— 迪米特法则

11.3 迪米特法则 > 位置 1845

迪米特法则(LoD) 也叫 最少 知识 原则。[J& DP]

迪米特法则(LoD),如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。[J&DP]

迪 米 特 法则 首先 强调 的 前提 是在 类 的 结构设计 上, 每一个 类 都应当 尽量降低成员的访问权限 [J& DP], 也就是说, 一个 类 包装 好 自己的 private 状态, 不需要 让 别的 类 知道 的 字段 或 行为 就不 要 公开。

迪 米 特 法则 其 根本思想, 是 强 调了 类 之 间的 松耦合

第 12 章 牛市股票还会亏钱?—— 外观模式

12.1 牛市股票还会亏钱? > 位置 1890

外观模式, 又叫门面模式

12.4 外观模式 > 位置 1909

外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。[DP]

外观 模式( Facade) 结构图 :

img

对于 面向 对象 有 一定 基础 的 朋友, 即使 没有 听说 过 外观 模式, 也 完全 有可 能在 很多 时候 使用 它, 因为 它 完美 地 体现 了 依赖倒转 原则 和 迪米特法则 的 思想, 所以 是非 常常 用的 模式 之一。

12.5 何时使用外观模式 > 位置 1921

那 外观 模式 在什么时候使用 最好 呢?

这要 分 三个 阶段 来说, 首先, 在 设计初期阶段, 应该 要有 意识 的 将 不同 的 两个 层 分离, 比如 经典 的 三层 架构, 就 需要 考虑 在 数据 访问 层 和 业务 逻辑 层、 业务 逻辑 层 和 表示 层 的 层 与 层 之间 建立 外观 Facade, 这样 可以 为 复杂 的 子系统 提供 一个 简单 的 接口, 使得 耦合 大大 降低。

其次, 在开发阶段, 子系统 往往 因为 不断 的 重 构 演化 而 变得 越来越 复杂, 大多数 的 模式 使用 时 也都 会 产生 很多 很 小的 类, 这本 是 好事, 但也 给 外部 调用 它们 的 用户 程序 带来 了 使 用上 的 困难, 增加 外观 Facade 可以 提供 一个 简单 的 接口, 减少 它们 之间 的 依赖。

第三, 在 维护 一个 遗留 的 大型 系统 时, 可能 这个 系统 已经 非常 难以 维护 和 扩展 了, 但 因为 它 包含 非常重 要的 功能, 新的 需求 开发 必须 要 依赖于 它。此时 用 外观 模式 Facade 也是 非常 合适 的。你 可 以为 新 系统 开发 一个 外观 Facade 类, 来 提供 设计 粗糙 或 高度 复杂 的 遗留 代码 的 比较 清晰 简单 的 接口, 让 新 系统 与 Facade 对象 交互, Facade 与 遗留 代码 交互 所有 复杂 的 工作。[R2P]

第 13 章 好菜每回味不同 —— 建造者模式

13.4 建造者模式 > 位置 2003

建造者(Builder)模式, 又叫 生成器模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。[DP]

13.4 建造者模式 > 位置 2029

建造者 模式 中 一个 很重 要的 类, 指挥者( Director), 用 它来 控制 建造 过程, 也 用 它来 隔离 用户 与 建造 过程 的 关联。

13.5 建造者模式解析 > 位置 2045

建造者 模式( Builder) 结构图 :

img

建造者模式 主要 是 用于 创建 一些 复杂 的 对象, 这些 对象 内部 构建 间的 建造 顺序 通常 是 稳定 的, 但对 象 内部 的 构建 通常 面临 着 复杂 的 变化。

第 14 章 老板回来,我不知道 —— 观察者模式

14.5 观察者模式 > 位置 2167

观察者模式 又叫 做 发布 – 订阅(Publish/ Subscribe)模式

观察者 模式( Observer) 结构图

img

Subject 类, 可翻译 为 主题 或 抽象 通知者, 一般用 一个 抽象 类 或者 一个 接口 实现。它 把 所有 对 观察者 对象 的 引用 保存 在 一个 聚集 里, 每个 主题 都可以 有 任何 数量 的 观察者。抽象 主题 提供 一个 接口, 可以 增加 和 删除 观察者 对象。

14.5 观察者模式 > 位置 2178

Observer 类, 抽象 观察者, 为所 有的 具体 观察者 定义 一个 接口, 在 得到 主题 的 通知 时 更新 自己。这个 接口 叫做 更新 接口。抽象 观察者 一般用 一个 抽象 类 或者 一个 接口 实现。更新 接口 通常 包含 一个 Update () 方法, 这个 方法 叫做 更新 方法。

14.6 观察者模式特点 > 位置 2210

当 一个 对象 的 改变 需要 同时 改变 其他 对象 , 而且 它不 知道 具体 有多 少 对象 有待 改变 时, 应该 考虑 使用 观察者 模式。

观察者 模式 所做 的 工作 其实 就是 在 解除耦合。让 耦合 的 双方 都 依赖于抽象, 而 不是 依赖于 具体。从而 使得 各自 的 变化 都不 会 影响 另 一边 的 变化。这 实在 是 依赖倒转 原则 的 最佳 体现 呀。

现实 编程 中, 具体 的 观察者 完全 有可能 是 风马牛 不相 及 的 类, 但 它们 都 需要 根据 通知者 的 通知 来 做出 Update() 的 操作, 所以 让 它们 都 实现 下面 这样 的 一个 接口 就可以 实现 这个 想法 了。

interface Observer { void Update(); }

14.9 事件委托说明 > 位置 2258

委托 就是 一种 引用 方法 的 类型。一旦 为 委托 分配 了 方法, 委托 将 与 该 方法 具有 完全 相同 的 行为。委托 方法 的 使用 可以 像 其他 任何 方法 一样, 具有 参数 和 返回 值。委托 可以 看作 是对 函数 的 抽象, 是 函数 的’ 类’, 委托 的 实例 将 代表 一个 具体 的 函数。

delegate void EventHandler (); 可以 理解 为 声明 了 一个 特殊 的’ 类’。而’ public event EventHandler Update;’ 可以 理解 为 声明 了 一个’ 类’ 的 变量。具体地说, 应该 是 声明 了 一个 事件 委托 变量 叫’ 更新’。

一个 委托 可以 搭载 多个 方法, 所有 方法 被 依次 唤起。更重 要的 是, 它可 以使 得 委托 对象 所 搭载 的 方法 并不 需要 属于 同一个 类。

第 15 章 就不能不换 DB 吗?—— 抽象工厂模式

15.2 最基本的数据访问程序 > 位置 2334

工厂方法模式 是 定义 一个 用于 创建 对象 的 接口, 让 子类 决定 实例 化 哪一个 类。

img

15.4 用了抽象工厂模式的数据访问程序 > 位置 2395

涉及 到 多个产品系列 的 问题, 有一个 专门 的 工厂 模式 叫 抽象工厂模式

15.5 抽象工厂模式 > 位置 2398

抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。[DP]

抽象 工厂 模式( Abstract Factory) 结构图

img

15.8 用反射 + 抽象工厂的数据访问程序 > 位置 2446

本来 依赖注入 是 需要 专门 的 IoC 容器 提供, 比如 Spring. NET, 显然 当前 这个 程序 不需要 这么 麻烦, 你 只 需要 再 了解 一个 简单 的. NET 技术’ 反射’ 就可以 了。

反射技术 格式 : Assembly.Load (“程序集名称”).Createlnstance (“命名空间。类名称”);

15.9 用反射 + 配置文件实现数据访问程序 > 位置 2486

所有 在用 简单 工厂 的 地方, 都可以 考虑 用反射 技术 来 去除 switch 或 if, 解除分支判断 带来 的 耦合。

第 16 章 无尽加班何时休 —— 状态模式

16.5 状态模式 > 位置 2577

状态模式 主要 解决 的 是 当 控制 一个 对象 状态 转换 的 条件 表达式 过于 复杂 时 的 情况。把 状态 的 判断 逻辑 转移 到 表示 不同 状态 的 一系列 类 当中, 可以 把 复杂 的 判断 逻辑 简化。

状态 模式( State) 结构图 :

img

16.6 状态模式好处与用处 > 位置 2588

状态 模式 的 好处 是将 与 特定 状态 相关 的 行为 局部化, 并且 将不 同 状态 的 行为 分割 开来 [DP]。

是不是 就是 将 特定 的 状态 相关 的 行为 都 放入 一个 对象 中, 由于 所有 与 状态 相关 的 代码 都存 在于 某个 ConcreteState 中, 所以 通过 定义 新的 子类 可以 很容易 地 增加 新的 状态 和 转换 [DP]。

说白 了, 这样做 的 目的 就是 为了 消除 庞大 的 条件 分支 语句。

当 一个 对象 的 行为 取决于 它的 状态, 并且 它 必须 在 运行 时刻 根据 状态 改变 它的 行为 时, 就可以 考虑 使用 状态 模式 了。另外 如果 业务 需求 某项 业务 有多 个 状态, 通常 都是 一些 枚举 常量, 状态 的 变化 都是 依靠 大量 的 多分 支 判断 语句 来 实现, 此时 应该 考虑 将 每一种 业务 状态 定义 为 一个 State 的 子类。

第 17 章 在 NBA 我需要翻译 —— 适配器模式

17.2 适配器模式 > 位置 2651

适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。[DP]

系统 的 数据 和 行为 都 正确, 但 接口 不符 时, 我们 应该 考虑 用 适配器, 目的 是 使 控制 范围 之外 的 一个 原有 对象 与 某个 接口 匹配。适配器 模式 主要 应用于 希望 复 用 一些 现存 的 类, 但是 接口 又与 复 用 环境 要求 不一致 的 情况, 比如 在 需要 对 早期 代码 复 用 一些 功能 等 应用 上 很有 实际 价值。

适配器 模式( Adapter) 结构图 :

img

17.3 何时使用适配器模式 > 位置 2687

通常 是在 软件 开发 后期 或 维护 期 再考虑 使用

那有 没有 设计 之初 就 需要 考虑 用 适配器 模式 的 时候?

当然 有, 比如 公司 设计 一 系统 时 考虑 使用 第三方 开发 组件, 而这 个 组件 的 接口 与我 们 自己的 系统 接口 是 不 相同 的, 而 我们 也 完全 没有 必要 为了 迎合 它 而 改动 自己的 接口, 此时 尽管 是在 开发 的 设计 阶段, 也是 可以 考虑 用 适配器 模式 来 解决 接口 不同 的 问题。

第 18 章 如果再回到从前 —— 备忘录模式

18.3 备忘录模式 > 位置 2771

备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。[DP]

备忘录 模式( Memento) 结构图 :

img

18.4 备忘录模式基本代码 > 位置 2787

是把 要 保存 的 细节 给 封 装在 了 Memento 中了, 哪 一天 要 更改 保存 的 细节 也不 用 影响 客户 端 了。

Memento 模式 比较 适用于 功能 比较 复杂 的, 但 需要 维护 或 记录 属性 历史 的 类, 或者 需要 保存 的 属性 只是 众多 属性 中的 一 小部分 时,

如果 在某 个 系统 中 使用 命令 模式 时, 需要 实现 命令 的 撤销 功能, 那么 命令 模式 可以 使用 备忘录 模式 来 存储 可撤销 操作 的 状态 [DP]。

有时 一些 对象 的 内部 信息 必须 保存 在 对象 以外 的 地方, 但是 必须 要由 对象 自己 读取, 这时, 使用 备忘录 可以 把 复杂 的 对象 内部 信息 对其 他的 对象 屏蔽 起来 [DP], 从而 可以 恰当 地保 持 封装 的 边界。

第 19 章 分公司 = 一部门 —— 组合模式

19.2 组合模式 > 位置 2844

组合模式(Composite),将对象组合成树形结构以表示’部分 – 整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。[DP]

组合 模式( Composite) 结构图:

img

19.4 何时使用组合模式 > 位置 2866

当你 发现 需求 中 是 体现 部分 与 整体 层次 的 结构 时, 以及 你 希望 用户 可以 忽略 组合 对象 与 单个 对象 的 不同, 统一 地 使用 组合 结构 中的 所有 对象 时, 就应 该 考虑 用 组合 模式 了。

19.6 组合模式好处 > 位置 2923

组合 模式 让 客户 可以 一致 地 使用 组合 结构 和 单个 对象。

第 20 章 想走?可以!先买票 —— 迭代器模式

20.2 迭代器模式 > 位置 2972

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。[DP]

当你 需要 访问 一个 聚集 对象, 而且 不管 这些 对象 是什么 都 需要 遍历 的 时候, 你就 应该 考虑 用 迭代 器 模式。

你 需要 对 聚集 有多 种 方式 遍历 时, 可以 考虑 用 迭代 器 模式。

迭代 器 模式 为 遍历 不同 的 聚集 结构 提供 如 开始、 下一个、 是否 结束、 当前 哪 一项 等 统一 的 接口。

不过 现今 来看 迭代 器 模式 实用 价值 远 不如 学习 价值 大 了, MartinFlower 甚至 在 自己的 网 站上 提出 撤销 此 模式。因为 现在 高级 编程 语言 如 C#、 JAVA 等 本身 已经 把这 个 模式 做 在 语言 中了。

哈, foreach in ,另外 还有 像 IEnumerable 接口 也是 为 迭代 器 模式 而 准备 的。

20.3 迭代器实现 > 位置 2987

迭代 器 模式( Iterator) 结构图:

img

20.4 .NET 的迭代器实现 > 位置 3009

IEumerator 支持 对 非 泛 型 集合 的 简单 迭代 接口。

img

IEnumerable 公开 枚举 数, 该 枚举 数 支持 在 非 泛 型 集合 上进 行 简单 迭代。

img

你会 发现, 这 两个 接口, 特别是 IEumerator 要比 我们 刚才 写的 抽象 类 Iterator 要 简洁, 但可 实现 的 功能 却 一点 不少, 这 其实 也 是对 GoF 的 设计 改良 的 结果。

foreach in 就是 实现 这 两个 接口 来 实际 循环 遍历:

C#

123456

IEnumerator<string> e = a.GetEnumerator(); while(e.MoveNext()){ Console.WriteLine("{0} 请买车票!", e.Current); }

迭代 器( Iterator) 模式 就是 分离 了 集合 对象 的 遍历 行为, 抽象 出 一个 迭代 器 类 来 负责, 这样 既可以 做到 不 暴露 集合 的 内部 结构, 又可 让 外部 代码 透明 地 访问 集合 内部 的 数据。

第 21 章 有些类也需计划生育 —— 单例模式

21.4 单例模式 > 位置 3103

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。[DP]

单 例 模式( Singleton) 结构图 :

img

21.4 单例模式 > 位置 3109

“我怎 么 感觉 单 例 有点 像 一个 实用类 的 静态 方法, 比如. Net 框架 里 的 Math 类, 有很 多数 学 计算 方法, 这 两者 有 什么 区别 呢?”

“你说 得 没错, 它们 之 间的 确 很 类似, 实用 类 通常 也会 采用 私有化 的 构造 方法 来 避免 其 有 实例。但它 们 还是 有很 多 不同 的, 比如 实用 类 不保 存 状态, 仅 提供 一些 静态 方法 或 静态 属性 让你 使用, 而 单例类 是有 状态 的。实用 类 不能 用于 继承 多 态, 而 单 例 虽然 实例 唯一, 却是 可以 有 子类 来 继承。实用 类 只不过是 一些 方法 属性 的 集合, 而 单 例 却是 有着 唯一 的 对象 实例。在 运用 中 还得 仔细 分析 再作 决定 用 哪一种 方式。”

21.5 多线程时的单例 > 位置 3115

多 线程 的 程序 中, 多个 线程 同时, 注意 是 同时 访问 Singleton 类, 调用 GetInstance() 方法, 会有 可能 造成 创建 多个 实例 的。

可以 给 进程 一把 来 处理。这里 需要 解释 一下 lock 语句 的 涵义, lock 是 确保 当 一个 线程 位于 代码 的 临界 区时, 另一个 线程 不进 入 临界 区。如果 其他 线程 试图 进入 锁定 的 代码, 则 它将 一直 等待( 即被 阻止), 直到 该 对象 被 释放。

21.6 双重锁定 > 位置 3126

不用 让 线程 每次 都 加锁, 而 只是 在 实例 未被 创建 的 时候 再加 锁 处理。同时 也能 保证 多 线程 的 安全。这种 做法 被称为 Double- Check Locking( 双重 锁定)。

21.7 静态初始化 > 位置 3136

其实 在 实际 应用 当中, C# 与 公共 语言 运行 库 也 提供 了 一种’ 静态初始化‘ 方法, 这种 方法 不需要 开发 人员 显 式 地 编写 线程 安全 代码, 即可 解决 多 线程 环境 下 它是 不安全 的 问题。[MSDN]

谈不上 更好, 只不过 实现 更简单。我们 来看 代码。

img

由于 这种 静态 初始化 的 方式 是在 自己 被 加载 时 就 将 自己 实例 化, 所以 被 形象 地 称之为 饿汉式单例类, 原先 的 单 例 模式 处理 方式 是 要在 第一次 被 引用 时, 才会 将 自己 实例 化, 所以 就被 称为 懒汉式单例类。[ J& DP]

由于 饿汉 式, 即 静态 初始化 的 方式, 它是 类 一 加载 就 实例 化 的 对象, 所以 要 提前 占用 系统 资源。然而 懒汉 式, 又会 面临 着 多 线程 访问 的 安全性 问题, 需要 做 双重 锁定 这样 的 处理 才可 以 保证 安全。所以 到底 使用 哪一种 方式, 取决于 实际 的 需求。从 C# 语言 角度 来讲, 饿汉 式 的 单 例 类 已经 足够 满足 我们 的 需求 了。

第 22 章 手机软件何时统一 —— 桥接模式

22.2 紧耦合的程序演化 > 位置 3237

在 面向 对象 设计 中, 我们 还有 一个 很重 要的 设计 原则, 那就 是 合成 / 聚合复用原则。即 优先 使用 对象 合成 / 聚合, 而 不是 类 继承 [DP]。

22.3 合成 / 聚合复用原则 > 位置 3239

合成( Composition, 也有 翻译 成 组合)聚合( Aggregation) 都是 关联 的 特殊 种类。 聚合 表示 一种 弱 的’ 拥有’ 关系, 体现 的 是 A 对象 可以 包含 B 对象, 但 B 对象 不是 A 对象 的 一部分; 合成 则是 一种 强的’ 拥有’ 关系, 体现 了 严格 的 部分 和 整体 的 关系, 部分 和 整体 的 生命 周期 一样 [DPE]。

img

合成 / 聚合 复 用 原则 的 好处 是, 优先 使用 对象 的 合成 / 聚合 将有 助于 你 保持 每个 类 被 封装, 并被 集中 在 单个 任务 上。这样 类 和 类 继承 层次 会 保持 较小 规模, 并且 不太 可能 增长 为 不可 控制 的 庞然大物 [DP]。

手机 品牌 包含 有手 机 软件, 但 软件 并不是 品牌 的 一部分, 所以 它们 之间 是 聚合 关系。

结构图(下一节用到):

img

22.4 松耦合的程序 > 位置 3289

两个 抽象 类 之间 有 什么, 像 什么?

有一个 聚合 线, 哈, 像 一座 桥。” “好, 说得 好, 这个 设计 模式 就 叫做’ 桥 接 模式’。

抽象与它的实现分离, 这 并不 是说, 让 抽象 类 与其 派生 类 分离, 因为 这 没有 任何 意义。实现 指的 是 抽象 类 和 它的 派生 类 用来 实现 自己的 对象 [DPE]。就 刚才 的 例子 而言, 就是 让’ 手机’ 既可以 按照 品牌 来 分类, 也可以 按照 功能 来 分类。

22.5 桥接模式 > 位置 3296

由于 实现 的 方式 有 多种, 桥 接 模式 的 核心 意图 就是 把这 些 实现独立出来, 让 它们 各自 地 变化。这就 使得 每种 实现 的 变化 不会 影响 其他 实现, 从而 达到 应对 变化 的 目的。

22.6 桥接模式基本代码 > 位置 3299

桥 接 模式( Bridge) 结构图 :

img

22.6 桥接模式基本代码 > 位置 3320

在 发现 我们 需要 多角度去分类实现对象, 而 只用 继承 会 造成 大量 的 类 增加, 不能 满足 开放 – 封闭 原则 时, 就 应该 要 考虑 用 桥 接 模式 了。

第 23 章 烤羊肉串引来的思考 —— 命令模式

23.6 命令模式 > 位置 3416

命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。[DP]

命令 模式( Command) 结构图 :

img

23.7 命令模式作用 > 位置 3435

命令 模式 的 优点

第一, 它 能 较 容易 地 设计 一个 命令 队列;

第二, 在 需要 的 情况下, 可以 较 容 易地 将 命令 记入 日志;

第三, 允许 接收 请求 的 一方 决定 是否 要 否决 请求。

第四, 可以 容易 地 实现 对 请求 的 撤销 和 重做;

第五, 由于 加进 新的 具体 命令 类 不 影响 其 他的 类, 因此 增加 新的 具体 命令 类 很容易。

其实 还有 最 关键 的 优点 就是 命令 模式 把 请求 一个 操作 的 对象 与 知道 怎么 执行 一个 操作 的 对象 分割 开。

敏捷开发原则 告诉 我们, 不要 为 代码 添加 基于 猜测 的、 实际 不需 要的 功能。如果不 清楚 一个 系统 是否 需要 命令 模式, 一般 就不 要 着急 去 实现 它, 事实上, 在 需要 的 时候 通 过重构 实现 这个 模式 并不 困难, 只有 在 真正 需要 如 撤销 / 恢复 操作 等 功能 时, 把 原来 的 代码 重 构 为 命令 模式 才有 意义。[R2P]

第 24 章 加薪非要老总批?—— 职责链模式

24.3 职责链模式 > 位置 3500

职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。[DP]

这里 发出 这个 请求 的 客户 端 并不 知道 这 当中 的 哪一个 对象 最终 处理 这个 请求, 这样 系统 的 更改 可 以在 不 影响 客户 端 的 情况下 动态 地 重新 组织 和 分配 责任。

职责 链 模式( Chain of Responsibility) 结构图 :

img

24.4 职责链的好处 > 位置 3515

接收者 和 发送者 都没 有 对方 的 明确 信息, 且 链 中的 对象 自己 也 并不 知道 链 的 结构。结果是 职责 链 可简化 对象 的 相互 连接, 它们 仅 需 保持 一个 指向 其 后继者 的 引用, 而 不需 保持 它 所有 的 候选 接受者 的 引用 [DP]。这也 就 大大 降低 了 耦 合度 了。

第 25 章 世界需要和平 —— 中介者模式

25.1 世界需要和平! > 位置 3552

中介者模式 又 叫做 调停者模式

尽管 将 一个 系统 分割 成 许多 对象 通常 可以 增加 其 可 复 用性, 但是 对象 间 相互 连接 的 激增 又会 降低 其 可 复 用性 了。

是因为 大量 的 连接 使得 一个 对象 不可 能在 没有 其他 对象 的 支持下 工作, 系统 表现 为 一个 不可分割 的 整体, 所以, 对 系统 的 行为 进行 任何 较大 的 改动 就 十分困难 了。

img

25.1 世界需要和平!> 位置 3574

通过 中介 者 对象, 可以 将 系统 的 网状 结构 变成 以 中介 者 为 中心 的 星形结构, 使得 系统 的 结构 不会 因为 新 对象 的 引入 造成 大量 的 修改 工作。

img

25.2 中介者模式 > 位置 3582

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

中介 者 模式( Mediator) 结构图 :

img

25.4 中介者模式优缺点 > 位置 3634

中介 者 模式 很容易 在 系统 中 应用, 也 很容易 在 系统 中 误用。当 系统 出现 了’ 多对 多’ 交互 复杂 的 对象 群 时, 不要 急于 使用 中介 者 模式, 而要 先 反思 你的 系统 在 设计 上 是不是 合理。

中介 者 模式 的 优点 来自 集中控制, 其 缺点 也 是它, 使用 时 是要 考虑 清楚 。

25.4 中介者模式优缺点 > 位置 3658

中介 者 模式 一般应用于 一组 对象 以 定义 良好 但是 复杂 的 方式 进行 通信 的 场合, 以及 想定 制 一个 分布 在 多个 类 中的 行为, 而又 不想 生成 太多 的 子类 的 场合。

第 26 章 项目多也别傻做 —— 享元模式

26.2 享元模式 > 位置 3703

享元模式(Flyweight),运用共享技术有效地支持大量细粒度的对象。[DP]

img

26.4 内部状态与外部状态 > 位置 3742

在 享 元 对象 内部 并且 不会 随 环境 改变 而 改变 的 共享 部分, 可以 称 为是 享 元 对象 的 内部 状态, 而 随 环境 改变 而 改变 的、 不可以 共享 的 状态 就是 外部 状态 了。事实上, 享 元 模式 可以避免 大量 非常 相似 类 的 开销。在 程序设计 中, 有时 需要 生成 大量 细 粒度 的 类 实例 来 表示 数据。如果 能 发现 这些 实例 除了 几个 参数 外 基本上 都是 相同 的, 有时 就能 够受 大幅度 地 减少 需要 实例 化 的 类 的 数量。如果 能把 那些 参数 移到 类 实例 的 外面, 在 方法 调用 时 将它 们 传递 进来, 就可以 通过 共享 大幅度 地 减少 单个 实例 的 数目。

26.5 享元模式应用 > 位置 3780

如果 一个 应用 程序 使用 了 大量的对象, 而 大量 的 这些 对象 造成了 很大 的 存储 开销 时 就应 该 考虑 使用;还有 就是 对象 的 大多数 状态 可以 外部 状态, 如果 删除 对象 的 外部 状态, 那么 可以 用 相对 较少 的 共享 对象 取代 很多 组 对象, 此时 可以 考虑 使用 享 元 模式。

26.5 享元模式应用 > 位置 3802

使用 享 元 模式 需要 维护 一个 记录 了 系统 已有 的 所有 享 元 的 列表, 而这 本身 需要 耗费 资源, 另外 享 元 模式 使得 系统 更加 复杂。为了 使对 象 可以 共享, 需要 将 一些 状态 外部 化, 这使 得 程序 的 逻辑 复杂化。因此, 应当 在 有 足够多的对象 实例 可供 共享 时 才 值得 使用 享 元 模式。

第 27 章 其实你不懂老板的心 —— 解释器模式

27.2 解释器模式 > 位置 3834

解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。[DP]

解释器 模式 需要 解决 的 是, 如果 一种 特定 类型 的 问题 发生 的 频率 足够 高, 那么 可能 就 值得 将 该 问题 的 各个 实例 表述 为 一个 简单 语言 中的 句子。这样 就可以 构建 一个 解释器, 该 解释器 通过 解释 这些 句子 来 解决 该 问题。

所谓 的 解释器 模式, 正则表达式 就是 它的 一种 应用, 解释器 为 正 则 表达式 定义 了 一个 文法, 如何 表示 一个 特 定的 正 则 表达式, 以及 如何 解释 这个 正 则 表达式。

解释器 模式( interpreter) 结构图:

img

27.3 解释器模式好处 > 位置 3875

当 有一个 语言 需要 解释执行, 并且 你 可将 该 语言 中的 句子 表示 为 一个 抽象 语法树 时, 可使用 解释器 模式。

用了 解释器 模式, 就 意味着 可以 很容易 地 改变 和 扩展 文法, 因为 该 模式 使用类来表示文法规则, 你 可使用 继承 来 改变 或 扩展 该文 法。也比 较 容易 实现 文法, 因为 定义 抽象 语法树 中 各个 节点 的 类 的 实现 大体 类似, 这些 类 都 易于 直接 编写 [DP]。

解释器 模式 也有 不足 的, 解释器 模式 为 文法 中的 每一 条规 则 至少 定义 了 一个 类, 因此 包含 许多 规则 的 文法 可能 难以 管理 和 维护。建议 当 文法 非常 复杂 时, 使用 其他 的 技术 如 语法 分析 程序 或 编译器 生成 器 来 处理 [DP]

第 28 章 男人和女人 —— 访问者模式

28.5 访问者模式 > 位置 4062

访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。[DP]

访问者 模式( Visitor) 结构图 :

img

在这里, Element 就是 我们 的’ 人’ 类, 而 ConcreteElementA 和 ConcreteElementB 就是’ 男人’ 和’ 女人’, Visitor 就是 我们 写的’ 状态’ 类, 具体 的 ConcreteVisitor 就是 那些’ 成功’、’ 失败’、’ 恋爱’ 等等 状态。至于 ObjectStructure 就是’ 对象 结构’ 类 了。

28.5 访问者模式 > 位置 4071

访问者 模式 适用于 数据 结构 相对 稳定 的 系统,它 把 数据 结构 和 作用于 结构上 的 操作 之间 的 耦合 解脱 开, 使得 操作 集合 可以 相对 自由 地 演化。

访问者 模式 的 目的 是要 把 处理 从 数据 结构 分离 出来。很多 系统 可以 按照 算法 和数 据 结构 分开, 如果 这样 的 系统 有 比较 稳定 的 数据 结构, 又有 易于 变化 的 算法 的 话, 使用 访问者 模式 就是 比较 合适 的, 因为 访问者 模式 使得 算法 操作 的 增加 变得 容易。反之, 如果 这样 的 系统 的 数据 结构 对象 易于 变化, 经常 要有 新的 数据 对象 增加 进来, 就不 适合 使用 访问者 模式。

访问者 模式 的 优点 就是 增加 新的 操作 很容易, 因为 增加 新的 操作 就 意味着 增加 一个 新的 访问者。访问者 模式 将 有关 的 行为 集中 到 一个 访问者 对象 中。

访问者 的 缺点 其实 也就是 使 增加 新的 数据 结构 变得 困难 了。

所以 GoF 四人 中的 一个 作者 就 说过:’ 大多 时候 你 并不 需要 访问者 模式, 但当 一旦 你 需要 访问者 模式 时, 那就 是真 的 需要 它 了。’ 事实上, 我们 很难 找到 数据 结构 不变化 的 情况, 所以 用 访问者 模式 的 机会 也就 不太 多了。这也 就是 为什么 你 谈到 男人 女人 对比 时 我很 高兴 和你 讨论 的 原因, 因为 人类 性别 这样 的 数据 结构 是 不会 变化 的。

第 29 章 OOTV 杯超级模式大赛 —— 模式总结

29.3 超模大赛开幕式 > 位置 4233

根据 模式 的 特点, 设置 了 三个 类别, 分别 是 创建型模式、 结构型模式 和 行为型模式, 但 由于 有 11 位 选择 了 行为 型 模式, 人数 过多, 所以 行为 型 模式 又 分 为了 两 组。

分组 情况:

img

29.4 创建型模式比赛 > 位置 4247

第一 组 创建 型 选手, 1 号 选手, 抽象 工厂 小姐, 她的 口号 是 提供 一个 创建 一系列 或 相关 依赖 对象 的 接口, 而无 需 指定 它们 具体 的 类。[DP]

1 号 选手 抽象工厂( Abstract Factory):

img

2 号 选手, 建造者 小姐, 她的 口号 是将 一个 复杂 对象 的 构建 与 它的 表示 分离, 使得 同样 的 构建 过程 可以 创建 不同 的 表示。[DP]

2 号 选手 建造者( Bulider) :

img

3 号 选手 工厂 方法 小姐 向我 们 走来, 她 声称 定义 一个 用于 创建 对象 的 接口, 让 子类 决定 实例 化 哪一个 类, 工厂 模式 使 一个 类 的 实例 化 延 迟到 其 子类。[DP]

3 号 选手 工厂方法( Factory Method):

img

4 号 选手 是 原型 小姐, 她的 意图 是 用 原型 实例 指定 创建 对象 的 种类, 并且 通过 拷贝 这些 原型 创建 新的 对象。[DP]

4 号 选手 原型( Prototype):

img

5 号 选手 出场, 单 例 小姐, 她 提倡 简捷 就是 美, 保证 一个 类 仅有 一个 实例, 并提 供 一个 访问 它的 全局 访问 点。[DP]

5 号 选手 单例( Singleton):

img

29.4 创建型模式比赛 > 位置 4280

创建 型 模式 隐藏 了 这些 类 的 实例 是 如何 被 创建 和 放在 一起, 整个 系统 关于 这些 对象 所 知道 的 是由 抽象 类 所 定义 的 接口。这样, 创建 型 模式 在 创建 了 什么、 谁 创建 它、 它是 怎么 被 创建 的, 以及 何时 创建 这些 方面 提供 了 很大 的 灵活性 [DP]。

内聚性 描述 的 是一 个例 程 内部 组成部分 之间 相互 联系 的 紧密 程度。而 耦合 性 描述 的 是一 个例 程 与其 他 例程 之间 联系 的 紧密 程度。软件 开发 的 目标 应该 是 创建 这样 的 例程:内部 完整, 也就是 高内聚, 而 与其 他 例程 之间 的 联系 则是 小巧、 直接、 可见、 灵活 的, 这 就是 松耦合 [ DPE]。

29.4 创建型模式比赛 > 位置 4306

通常 设计 应 该是 从 工厂 方法 开始, 当 设计者 发现 需要 更大 的 灵活性 时, 设计 便会 向 其他 创建 型 模式 演化。当 设计者 在 设计 标准 之间 进行 权衡 的 时候, 了解 多个 创建 型 模式 可以 给 设计者 更多 的 选择 余地。

29.5 结构型模式比赛 > 位置 4341

第二 组, 也就是 结构型 模式 组

6 号 选手, 适配器 小姐, 她的 口号 是将 一个 类 的 接口 转换 成 客户 希望 的 另外 一个 接口。适配器 模式 使得 原本 由于 接口 不兼容 而 不能 一起 工作 的 那些 类 可以 一起 工作。[DP]

img

7 号 选手 叫 桥 接。 桥接 小姐 提倡 的 是将 抽象 部分 与 它的 实现 部分 分离, 使它 们 都可以 独立 地 变化。

img

8 号 选手 向我 们 走来, 组合 小姐, 一个 非常 美丽 的 姑娘, 她的 口号 是将 对象 组 合成 树 形 结构 以 表示’ 部分 – 整体’ 的 层次 结构, 组合 模式 使得 用户 对 单个 对象 和 组合 对象 的 使用 具有 一致性。[DP]

img

9 号 选手, 装饰 小姐, 她的 意图 非常 简单, 就是 动态 地 给 一个 对象 添加 一些 额外 的 职责。就 增加 功能 来说, 装饰 模式 相比 生成 子类 更加 灵活 [DP]。

img

10 号 选手 出现 了, 外观 小姐, 她的 形象 如 她的 名字 一样 的 棒, 她说 为 子系统 中的 一组 接口 提供 一个 一致 的 界面, 外观 模式 定义 了 一个 高层 接口, 这个 接口 使得 这一 子系统 更加 容易 使用。[DP]

img

11 号 选手 是 享元 小姐, 她的 参赛 宣言 为 运用 共享 技术 有效地 支持 大量 细 粒度 的 对象。[DP]

img

12 号 选手, 代理 小姐 向我 们 走来, 她 声称 为 其他 对象 提供 一种 代理 以 控制 对这 个 对象 的 访问。[DP]

img

29.5 结构型模式比赛 > 位置 4434

“哦, 各位 来宾, 观众 朋友们, 第二 场 结构 型 模式 的 比赛 真是 相当 精彩, 各位 选手 也都 实力 相当, 难分 伯仲, 现在 出现 了’ 桥接’、’ 适配器’、’ 外观’ 的 比分 均为 两分 的 相同 情况。根据 比赛规则, 她们 三位 需要 站上 PK 台, 进行 PK。三位 有请。”

“下面 请 三位 各自 说 一说 你 比 其他 两位 优秀 的 地方。适配器 小姐 先来。”

适配器 说:” 我 主要 是 为了 解决 两个 已有 接口 之间 不匹配 的 问题, 我不 需要 考虑 这些 接口 是 怎样 实现 的, 也不 考虑 它们 各自 可能 会 如何 演化。我的 这种 方式 不需 要对 两个 独立 设计 的 类 中 任一 个 进行 重新 设计, 就 能够使 它们 协同 工作。[DP]”

“非常好, 下面 有请 桥接 小姐。”

“我 觉得 我和 适配器 小姐 具有 一些 共同 的 特征, 就是 给 另一 对象 提供 一定程度 的 间接 性, 这样 可以 有利于 系统 的 灵活性。但 正 所谓 未雨绸缪, 我们 不能 等到 问题 发生了, 再去 考虑 解决问题, 而是 更应该 在 设计 之初 就 想好 应该 如何 做 来 避免 问题 的 发生, 我 通常 是在 设计 之初, 就 对 抽象 接口 与 它的 实现 部分 进行 桥 接, 让 抽象 与 实现 两者 可以 独立 演化。显然, 我的 优势 更 明显。[ DP]”

“OK, 说 得很 棒, 外观 小姐, 您有 什么 观点?”

“首先 我 刚 听完 两位 小姐 的 发言, 我 个人 觉得 她们 各自 有 各自 的 优点, 并不 能说 设计 之初 就 一定 比 设计 之后 的 弥补 要好, 事实上, 在 现实 中, 早已 设计 好的 两个 类, 过后 需要 它们 统一 接口, 整 合为 一 的 事例 也 比比皆是。因此 桥 接和 适配器 是 被用 于 软件 生命 周期 的 不同 阶段, 针对 的 是不同 的 问题, 谈不上 孰 优 孰 劣。然后, 对于 我 来说, 和 适配器 还有 些 近似, 都是 对现 存 系统 的 封装, 有 人说 我 其实 就是 另外 一组 对象 的 适配器, 这种 说法 是 不准确 的, 因为 外观 定义 的 是一 个 新的 接口, 而 适配器 则是 复 用 一个 原有 的 接口, 适配器 是 使 两个 已有 的 接口 协同 工作, 而外 观 则是 为 现存 系统 提供 一个 更为 方便 的 访问 接口。如果 硬要 说 我是 适配, 那么 适配器 是 用来 适配 对象 的, 而我 则是 用来 适配 整个 子系统 的。也就是说, 我所 针对 的 对象 的 粒度 更大。[ DP]”

29.6 行为型模式一组比赛 > 位置 4471

第三 组, 也就是 行为型 模式 一组

13 号 选手, 观察者 小姐 入场, 它的 口号 是 定义 对象 间的 一种 一对 多的 依赖 关系, 当 一个 对象 的 状态 发生 改变 时, 所有 依赖于 它的 对象 都得 到 通知 并被 自动 更新。[DP]

img

14 号 选手, 模板方法 小姐, 她 提倡 定义 一个 操作 的 算法 骨架, 而将 一些 步骤 延迟 到 子类 中, 模板 方法 使得 子类 可以 不改 变 一个 算法 的 结构 即可 重 定义 该 算法 的 某些 特定 步骤。[DP]

img

15 号 选手 是 命令 小姐, 它 觉得 应该 将 一个 请求 封装 为 一个 对象, 从 而使 你 可用 不同 的 请求 对 客户 进行 参数 化;可以 对 请求 排队 或 记录 请求 日志, 以及 支持 可撤销 的 操作。[DP]

img

16 号 是 状态 小姐, 她说 允许 一个 对象 在 其内 部 状态 改变 时 改变 它的 行为, 让 对象 看起来 似乎 修改 了 它的 类。[DP]

img

17 号 选手, 职责链 小姐, 她 一直 认为 使 多个 对象 都有 机会 处理 请求, 从而 避免 请求 的 发送者 和 接收者 之间 的 耦合 关系。将 这些 对象 连成 一条 链, 并 沿着 这条 链 传递 该 请求, 直到 有一个 对象 处理 它 为止。[DP]

img

29.7 行为型模式二组比赛 > 位置 4563

18 号 选手, 解释器 小姐, 它 声称 给定 一个 语言, 定义 它的 文法 的 一种 表示, 并 定义 一个 解释器, 这个 解释器 使用 该 表示 来 解释 语言 中的 句子。[DP]

img

19 号 选手 是 中介者 小姐, 她说 她是 用 一个 中介 对象 来 封装 一系列 的 对象 交互。中介 者 使 各 对像 不需要 显 式 地 相互 引用, 从而 使其 耦合 松散, 而且 可以 独立 地 改变 它们 之间 的 交互。[DP]

img

20 号 小姐 向我 们 走来, 访问者 小姐, 她 表示 一个 作用于 某 对象 结构 中的 各 元素 的 操作。它 使 你 可以 在 不 改变 各 元素 的 类 的 前提 下定义 作用于 这些 元素 的 新 操作。[DP]

img

21 号 小姐 是 策略, 一个 可爱 的 姑娘, 她的 意图 是 定义 一系列 的 算法, 把 它们 一个 个 封装 起来, 并且 使 它们 可 相互 替换。本 模式 使得 算法 可 独立 于 使用 它的 客户 而 变化。[DP]

img

22 号 选手, 备忘录 小姐, 她说 在 不破 坏 封装 性的 前提下, 捕获 一个 对象 的 内部 状态, 并在 该 对象 之外 保存 这个 状态。这样 以后 就 可将 该 对象 恢复 到 原先 保存 的 状态。[DP]

img

23 号选手, 迭代器 小姐, 她说, 提供 一种 方法 顺序 访问 一个 聚合 对象 中 各个 元素, 而又 不需 暴露 该 对象 的 内部 表示。[DP]

img

29.8 决赛 > 位置 4647

好的, 现在 我们 比赛 已经 进入 了 最高潮, 23 位 选手, 经过 激烈 的 比拼, 现在 选出 了 五位 选手 将 站在 PK 台上, 来 决定 今天 冠 亚 季军 的 归属。她们 分别 是…… 工厂 方法 小姐、 外观 小姐、 观察者 小姐、 策略 小姐 和 观众 朋友 选出 的 可爱 的 适配器 小姐。

img

29.8 决赛 > 位置 4720

面向 对象 设计 模式 体现 的 就是 抽象 的 思想, 类 是什么, 是对 对象 的 抽象, 抽象类 呢, 其实 就 是对 类 的 抽象, 那 接口 呢, 说白 了 就是 对 行为 的 抽象。

附录 A 培训实习生 —— 面向对象基础

A.7 继承 > 位置 4895

子类 从 它的 父 类 中 继承 的 成员 有方法、 域、 属性、 事件、 索引 指示器, 但对 于 构造 方法, 有 一些 特殊, 它不 能被 继承, 只能 被 调用。对于 调用 父 类 的 成员, 可 以用 base 关键字。

A.8 多态 > 位置 4935

多 态 表示 不同 的 对象 可以 执行 相同 的 动作, 但要 通过 它们 自己的 实现 代码 来 执行。

A.8 多态 > 位置 4940

这 里面 有 几点 注意, 第一, 子类父类 的 身份 出现,

第二、 子类 在 工作 时 以 自己的 方式 来 实现,

第三、 子类 以 父 类 的 身份 出现 时, 子类 特有 的 属性 和 方法 不可以 使用。

A.8 多态 > 位置 4960

多 态 的 原理 是 当 方法 被 调用 时, 无论 对象 是否 被 转换 为 其父 类, 都 只有 位于 对象 继承 链 最末端 的 方法 实现 会被 调用。也就是说, 虚 方法 是 按照 其 运行时 类型 而非 编译 时 类型 进行 动态 绑 定调 用的。[AMNFP]

A.10 抽象类 > 位置 4991

我们 完全可以 考虑 把 实例 化 没有 任何 意义 的 父 类, 改成 抽象 类, 同样 的, 对于 Animal 类 的 getShoutSound 方法, 其实 方法 体 没有 任何 意义, 所以 可以 将 virtual 修饰 符 改为 abstract, 使之 成为 抽象 方法。C# 允许 把 类 和 方法 声明 为 abstract, 即 抽象 类 和 抽象 方法。

抽象类 需要 注意 几点:

第一, 抽象 类 不能 实例 化;

第二, 抽象 方法 是 必须 被子 类 重写 的 方法,其实 抽象 方法 可以 被 看成 是 没有 实现 体 的 虚 方法;

第三, 如 果类 中 包含 抽象 方法, 那么 类 就必须 定义 为 抽象 类, 不论 是否 还 包含 其他 一般 方法。

A.11 接口 > 位置 5019

接口 是把 隐式 公共 方法 和 属性 组合 起来, 以 封装 特定 功能 的 一个 集合。一旦 类 实现 了 接口, 类 就可以 支持 接口 所指 定的 所有 属性 和 成员。声明 接口 在 语 法上 与 声明 抽象 类 完全 相同, 但不 允许 提供 接口 中 任何 成员 的 执行 方式。所以 接口 不能 实例 化, 不能 有 构造 方法 和 字段;不能 有 修饰 符, 比如 public、 private 等;不能 声明 虚拟 的 或 静态 的 等。还有 实现 接口 的 类 就必须 要 实现 接口 中的 所有 方法 和 属性。

A.11 接口 > 位置 5040

抽象 类 可以 给出 一些 成员 的 实现, 接口 却不 包含 成员 的 实现, 抽象 类 的 抽象 成员 可被 子类 部分 实现, 接口 的 成员 需要 实现 类 完全 实现, 一个 类 只能 继承 一个 抽象 类, 但可 实现 多个 接口 等等。但这 些 都是 从 两者 的 形态 上去 区分 的。

我 觉得 还有 三点 是 能 帮助 我们 去 区分 抽象 类 和 接口 的。

第一, 类 是对 对象 的 抽象;抽象 类 是对 类 的 抽象;接口 是对 行为 的 抽象。接口 是对 类 的 局部( 行为) 进行 的 抽象, 而 抽象 类 是对 类 整体( 字段、 属性、 方法) 的 抽象。如果 只 关注 行为 抽象, 那么 也可以 认为 接口 就是 抽象 类。

第二, 如果 行为 跨越 不 同类 的 对象, 可使用 接口;对于 一些 相 似的 类 对象, 用 继承 抽象 类。

第三, 从 设计 角度 讲, 抽象 类 是 从 子类 中 发现 了 公共 的 东西, 泛 化 出 父 类, 然后 子类 继承 父 类, 而 接口 是 根本 不知 子类 的 存在, 方法 如何 实现 还不 确认, 预先 定义。

抽象 类 是 自 底 而上 抽象 出来 的, 而 接口 则是 自 顶 向下 设计 出来 的。

A.14 委托与事件 > 位置 5133

委托 是对 函数 的 封装, 可以 当作 给 方法 的 特征 指定 一个 名称。而 事件 则是 委托 的 一种 特殊 形式, 当 发生 有意义 的 事情 时, 事件 对象 处理 通知 过程 [PC#]。 事件 其实 就是 设计 模式 中 观察者模式 在. NET 中的 一种 实现 方式。