发现类---面向对象领域(Domain)的分析

翻译:王咏武

 设计过程简介

  ● 通过考察问题领域(Domain)来识别出对象和类
    -- 通常对象(Object)应该首先被识别出来
    -- 把对象分类就可以形成类(Class)
  ● 识别出对象之间以及类之间的关系
    -- 结构型关系(Structural relationship)
    -- 协作型关系(Collaborative relationship)
  ● 分配职责
    -- 依据协作型关系分配
  ● 不断重复(Iterate)上述过程
  ● 应该为问题领域建模,而不是为解决方案(Solution)建模

 职责驱动的设计

  ● 是面向对象分析最广泛采用的方法
    -- 基于"客户机/服务器(Client/Server)关系
  ● 通常“客户机/服务器”有两种含义:
    -- 用于分布式系统,服务器提供对共享资源的访问,客户机提供用户界面
    -- 用于面向对象系统,服务器是提供服务的对象
    -- 我们使用第二种含义
  ● 客户机和服务器合作完成任务
    -- 一个对象可以在一种关系中是客户机,而在另一种关系中是服务器
  ● 服务器的职责是提供某些服务
  ● 通常,一个对象的行为应该是确定的

 CRC卡

  ● CRC方法为每个类制作一张索引卡,卡上标明该类的职责,以及完成每个职责需发生关系的其他类。在卡的背面写上对该类的简要描述
  ● 在下面的例子中,为了完成职责"Do something",类Foo必须和类X和Y协作(发送消息给它们)

 实例:"小棍"游戏

  ● 该程序是一种计算机上的两人玩的游戏。有一些按行排列的小棍,当游戏开始时,这些小棍按照如下排列:
        1:|
        2:| |
        3:| | |
        4:| | | |

 游戏规则:

  ● 游戏者依次进行,每人每次可以从一个非空行拿走一个或多个小棍,拿到最后一根小棍的游戏者输
  ● 游戏开始时,以及每次移动后,程序会显示游戏的状态,哪个游戏者将进行移动,他想移动的那行的小棍数量,以及他想移动的棍数
  ● 当移动非法时程序会提示用户(例如:希望移动的数目超过该行中的现有数量)
  ● 思考:
    -- 提取对象和类
    -- 使用CRC卡
    -- 谁应该负责跟踪游戏者的移动次序?

 行(Row)的CRC卡

 寻找对象和类

  ● 从需求/用例说明书开始,列出所有的名词,这些都是初始的候选对象和类
    -- 对每个候选对象,建议形成一个类
    -- 确定概念上和物理上的实体(Entity)
    -- 消除重复(代表相同事物的名词)
  ● 细化候选类,代表一个类的初始状态的名词应该是该类的属性
    -- 形容词可能是属性或子类
    -- 分离大而复杂的类,合并过小的类
  ● 寻找关系
    -- 列出所有的动词,它们是主语名词所代表的类的候选行为或职责
  ● 剔除系统外的事物
    -- 明确什么是系统的一部分
    -- 用户不应该是系统的一部分
  ● 问问自己:
    -- 为了使软件在未来易于扩展需要什么样的底层结构(Infrastructure)?
  ● 注意:好的流程有助于创建好的模型

 从问题域(Domain)到模型(Model)

  ● 每个真实世界的系统应该按下列几个明显的问题域设计:
    -- 业务问题(Business problem)
    -- 数据的持久化(Persistent)存储
    -- 用户界面
    -- 系统上下文(Context)
    -- 功能划分
  ● 注意:不要混淆不同问题域中的职责

 分层的架构模型

 模型中的物理实体

  ● 对于控制物理设备的软件特别重要,即什么情况下软件需要为物理对象提供一个好的模型
  ● OO编程起源于用一种好的方式写一个模拟器,即程序试图精确地再现某些真实的物理流程
  ● 例子:
    -- 问题域实体(Entity):汽车、飞机…
    -- 系统实体:打印机、调制解调器、传感器、制动器…

 模型中的概念实体

  ● 优先权,访问权限,数据格式转换,交易管理,内存管理,错误管理,工作流,数据结构(图、树、队列、堆栈、哈希表),数学对象,应用,底层结构

 建模类

  ● 动物
    -- 狗
      ※ 德国牧羊狗
      ※ 狮子狗
      ※ 野狗
    -- 猫
  ● 磁介质
    -- 磁盘
    -- 磁带
  ● 按照自然分类通常会形成好的继承树

 建模对象组

  ● 游戏布局
    -- 行(Row)
      ※ 小棍
  ● 集团
    -- 公司
      ※ 部门
        ◆ 雇员
  ● 对象的组应按照组合(Compsition)或聚合(Aggregation)关系建模,集合(Collection)是一种特殊的组

 建模执行(Executive)或控制(Controller)实体

  ● 通常需要一个对象来管理(coordinate)其他对象。由需求说明书中的被动语态的动词暗示
  ● 例子:事件管理器、游戏裁判

 建模你必须调用的模型外的事物

  ● 这一点在真实世界中非常重要
  ● 例子:
    -- 遗留代码和数据源
    -- 操作系统
    -- 第三方软件包
    -- 系统接口
  ● 标示外部事物提供的服务
  ● 生成提供服务的对象
  ● 考虑为远程服务提供一个代理(Proxy)对象来封装所有的分布式通讯

 检查你的类

  ● 每个类都应该有一个清晰的、易于理解的名字,这个名字应该听起来象一件事物,而不是一个功能
  ● 每个类都应该有一个清晰的、易于理解的意图,该意图能适用于它的所有子类
  ● 相似但不同的类建议用继承,或者有可能的话代理到一个第三方的共享类
  ● 当多个类有继承关系时,它们才可能相互覆盖(Overlap),否则类之间不可能有覆盖关系。此时一个类完全覆盖其它的类
  ● 不同的类可能合作来完成职责
  ● 类应该有语义相关的属性和方法

 结构型关系

  ● 组合
    -- B是A的一部分
    -- A拥有B
    -- A包含B
    -- A拥有B的一个集合(Collection)
  ● 子类/超类
    -- A是B的一种
    -- A是B的泛化(Generalization)
    -- A是B的特殊情况
    -- A的行为象B

 协作型关系

  ● A依赖于B
  ● A使用B
  ● A代理B
  ● A需要B的帮助
  ● A通知B
  ● A和B地位相同

 职责和协作

  ● 职责:
    -- 一个类的实例提供给其他对象的服务
    -- 一个类的所有实例有相同的职责
    -- 职责包括:
      ※ 执行动作(方法、行为)
      ※ 维护和提供信息(状态、属性)
      ※ 约束条件
  ● 协作:
    -- 客户机/服务器关系
    -- 协作是某一个类调用其他类来协助完成职责

 职责驱动的设计

  ● 类的职责定义了它的公共接口
    -- 浏览用例,寻找对象间的交互
    -- 对这些交互,确认客户机/服务器关系
    -- 每个交互暗示着服务器类的一个职责
    -- 从客户的角度用一个短语来表达职责,这就是支持该职责的服务器类的方法的名字
    -- 如果需要的话创建新的类
      ※ 大的职责暗示服务器类应该把一些工作委派(Delegate)给其他类
      ※ 如果一个客户需要不属于任何一个已存在的类的服务时,创建一个新的类

 分配职责

  ● 从这一步开始思考程序如何工作
  ● 铺开所有信息,行为和接口
  ● 尽可能的描述职责
  ● 保持信息的独立
  ● 职责可以被共享或代理
  ● 拿不准时,创建新类或删除旧类
  ● 没有任何职责的类是多余的
  ● 当你的设计太复杂时就重构它
  ● 检查设计看是否能支持所有的功能
    -- 按照用例,模拟模型的行为
  ● 不断迭代,重复

 实例:微波炉模拟器

  ● 需求描述:
    -- 微波炉通过微波照射来加热食物,用户使用键盘设定加热时间和微波功率,键盘上还有一个开始按钮。当时间到时,微波炉会停止并且响铃。如果门打开微波炉也会停止。假设微波发生器能够控制电子速调管在一秒的工作周期中工作
  ● 请为该系统寻找类

 微波炉-用例

  ● 设置时间
    -- 用户输入一个数字,然后按设置时间按钮
    -- 系统显示剩余的时间
    -- 系统设置时间
  ● 设置微波功率
    -- 用户输入一个数字,然后按设置微波功率按钮
    -- 如果数字不在1-10之间,什么也不做
    -- 系统显示功率级别
    -- 系统设置微波发生器
  ● 开门
    -- 停止时间计数
    -- 禁止微波发生器生成微波
  ● 关门
    -- 启动微波生成器
  ● 按开始按钮
    -- 如果门是关闭状态
    -- 开始计数
    -- 当时间大于0,生成微波
    -- 不断显示剩余的时间
    -- 当时间等于0,停止并响铃

 微波炉-类

  ● 微波炉
  ● 微波发生器
  ● 计时器
  ● 时钟
  ● 键盘
  ● 开始按钮
  ● 门
  ● 铃
  ● 其它按钮:清除,数字0-9,设置时间,设置微波功率
  ● 电子速调管
  ● 显示器

 微波炉-架构

  ● 用户界面中有三种类型的事件:
    -- 按钮被按下
    -- 门被打开或被关上
    -- 从时钟芯片来的计时事件
  ● 我们不想持续检查门来看它是开还是关,也不想持续检查时钟来看已过去多少时间。一种办法是当门打开或关上时,或时钟走过一个单位时,通知微波炉
  ● 事件驱动的架构好于轮询的架构
  ● 先假设你感兴趣的事件会造成你的类里的方法被调用。先不管如何来实现这一点
  ● 我们希望微波炉的所有部件(计时器、键盘等)能被其他种类的炉子重用。当然微波炉本身不能被重用,因为它和其他种类的炉子不同
  ● 按钮类没有系统控制的职责。我们会保持按钮类简单,它只有一个统一的接口函数:Push()。再次强调,我们希望有一个可重用的设计。因此,我们不想让按钮类关心系统中的其他部件

 微波炉-职责

  ● 微波炉本身:
    -- 知道系统的状态
    -- 把按钮命令职责委派给聚合对象
    -- 当门打开或关上时得到通知
      ※ 关闭微波发生成器
    -- 当时间减到0时得到通知
      ※ 关闭生成器
      ※ 响铃
    -- 当时间走一个单位时得到通知
      ※ 更新显示
      ※ 通知微波发生器
  ● 所有按钮:
    -- 被按时,送一个命令给微波炉
  ● 微波发生器:
    -- 知道功率强度
    -- 如果启动,按照微波级别生成微波
    -- 当时间走一个单位时得到通知
      ※ 按照微波强度(1-10之间的整数)在一个微波周期激活电子速调管
  ● 计时器:
    -- 知道剩余的时间
    -- 计数
    -- 通知微波炉下列事件:
      ※ 时钟走了一个单位(十分之一秒)
      ※ 剩余时间为0
  ● 显示器:
    -- 显示剩余的时间
    -- 显示微波强度
    -- 知道并且显示键盘当前输入的数字
  ● 键盘:
    -- 按钮的容器类。注意:这是一个不确定的职责,从下一次迭代的模型中删除键盘类
  ● 门:
    -- 通知微波炉开门和关门事件
  ● 铃:
    -- 响铃

 微波生成器的CRC卡

 UML预览:微波炉

  ● 比较两种不同的微波炉系统的UML类图
  ● 注意:UML类图是静态的,所以它不能用来对系统的动态行为建模

 微波炉类图1

  ● 轮询架构
  ● 工作良好,但是…
  ● 需要两个线程
  ● 高度的对象耦合
  ● 复杂
  ● 键盘不可重用
  ● 计时器不可重用
  ● …

 

 微波炉类图2

 比较两个微波炉模型

  ● 模型2有一个微波炉类(Mediator设计模式的一个例子),注意各部件之间的协作非常简单,因为它们是松散耦合的,所以更独立
  ● 模型1使用轮询架构,而模型2采用事件驱动
  ● 模型2根本没有一个键盘类,因为该类没有任何职责,但是在模型1,它有职责
  ● 模型1和模型2处理10个数字按钮的方式不同
  ● 模型1有一个秒表(StopWatch)类
  ● 模型2不但简单而且更灵活

 词汇表

  ● 问题域                   Domains
  ● 职责                    Responsibilities
  ● 协作                    Collaborations
  ● 代理,委派                 Delegations
  ● 重构                    Refactoring
  ● 关系                    Associations
    -- 特化 / 泛化 / 继承           Specialization / Generalization / Inheritance
    -- 协作 / 聚合 / 组合 / 集合 / 依赖    Collaborations / Aggregations / Compositions / Collections / Dependencies
  ● 组                     Group
    -- 聚合 / 组合 / 集合           Aggregations / Compositions / Collections
  ● 遗留系统                  Legacy Systems
  ● 代理                    Proxies
  ● 中间人设计模式               The Mediator design pattern


posted on 2004-04-20 11:29  大树啊  阅读(1765)  评论(0编辑  收藏  举报

导航