## 7.4 面向对象设计*
理解了面 向 对象的基 本 概念之后 , 就可以应 用 这些概念 来 进行面向 对 象 设 计(object-oriented design,简称 OOD)。 传统的程序设计方法是结构化的自顶向下设计,其思想是将软件系统分解为若干个功能,
每个功能都是对数据的一个操作过程。功能又可以划分为若干个子功能,如此反复分解下去, 直至每个功能所对应的操作过程显而易见为止。在分解功能的同时,还要考虑各功能之间的 接口。这种方法是以过程(函数)为中心的,每个函数都是一个黑盒子,函数调用者只需了 解函数的功能,无需知道实现功能的细节。
面向对象设计是以数据为中心来展开的。对于某种客观实体的数据,考虑可能施加在数 据上的各种操作,然后将数据和操作封装成一个黑盒子——对象。对象通过界面向外界提供 数据操作服务,服务的使用者只需了解服务接口,不必关心服务的实现细节。即使改动了对 象内部的实现细节,只要服务接口不变,所有使用该服务的程序代码就不需要改变。同样地, 对象作为服务提供者,也不需要考虑它的服务将被使用者如何使用,只需确保其服务能正确 处理数据即可。
因此,OOD 的中心任务就是设计各种对象,将对象的数据和行为用类定义出来。OOD 将一个复杂问题分解成一组相互协作的类,以类为设计单位可以大大减小设计的复杂性。
下面是 OOD 的一些指导准则。
描述问题
面向对象技术专家 G. Booch 提出了一种基于词性分析的设计方法,该方法要求设计人员 从目标系统的描述出发,通过问题描述中的名词和动词,来发现候选对象及对象方法。因此, OOD 的第一步是要建立待解决问题的准确、无二义性的描述。问题描述应该是自然、客观的, 不要加入人工的、主观的因素,这是因为面向对象思想的宗旨就是按客观世界的本来面目来 建模并开发应用系统。
找出候选对象
我们的目标是找出有助于解决问题的对象。从问题描述入手,找出问题描述中的名词, 然后逐个考察这些名词,看看是否合适作为对象。对象一般都包含一组相关数据项,如学生(包含学号、姓名、年龄等数据项)、课程(包含课程名、学分、教材等数据项)。而能用单 一数据表示的,或者只有单一行为的实体,一般不适合作为对象,如人数、姓名等。
注意,由于人类可以同时考虑和理解的问题数目受到大脑记忆和处理能力的制约,因此 认定的对象数目不宜过多。合适的对象通常是问题中自然出现的而非人工生硬构造的实体, 而且对象应该向外界提供足够复杂的服务。
确定对象的数据属性
对于认定的对象,接下来就该确定对象的数据。能确定为对象数据的信息一般都具有普遍性,即所有同类对象都拥有的数据,例如学生的学号和姓名。另外,对象数据必须对解决 问题有用,例如,学生的学号、姓名都是信息管理应用中必需的信息,而学生的发型可能就 与应用无关。注意,对象的数据可能是学号、姓名这样的基本数据类型值,也有可能是复杂 类型的值,甚至可能是另一种对象。例如,学生选修的课程也是属于学生对象的数据,课程 本身也是对象。
确定对象的行为属性
认定了对象及其数据之后,接着考虑对象需要什么操作。我们从问题描述中找出动词,它们通常描述了对象的行为。例如,“学生选修课程”中的“选修”就是学生对象的行为。对 象的方法通常使用动词来命名。
一类常见的方法是对实例变量的读取和设置方法。假设对象有实例变量 value,则通常应 提供 getValue 和 setValue 方法。这是因为对象数据是隐藏的,外界只能通过对象的方法来操 作对象数据。
对象的方法就是对象向外界提供的界面。界面不完善(如缺少某些方法)会使对象的使 用者无从完成工作,但也不是说向外提供的方法越多越好。对象的界面设计应当遵循恰好够 用的原则,即在能满足用户需要的条件下,界面越简洁越好。
实现对象的方法
有些方法用寥寥数行代码就能实现,有些方法则可能需要设计复杂的算法来实现。对于复杂方法,可以利用自顶向下逐步求精方法来设计。 在方法实现过程中,可能发现一个对象与其他对象之间的新的交互,这会提示我们为类增加新方法,或者增加新的类。
迭代设计
对于复杂程序设计,没有人能够一次性地顺利走过设计全过程。在设计过程中,经常需 要在设计、测试、增加新类或为现有类增加新方法等步骤之间循环往复。
应当多尝试替代方案,即使一个设计方案看上去不太灵,也可以沿着该方案的方向试着 前行,看看会导致什么结果。好的设计一定是通过大量试错而得到的,正是因为错误的设计 才使我们明白应该设计什么样的系统。
最后要指出,上述基于名词动词分析的 OOD 方法只是一个简单的策略,真正进行类的 设计时情况往往很复杂。究竟是否应当设计某个类并没有绝对的设计准则,经常依赖于设计 者的经验。和任何别的设计一样,程序设计既是艺术也是经验,可以通过不断的实践来掌握设计方法。
还要指出,对于小程序,OOD 一般起不了什么作用,说不定反而使编程变得麻烦。但当 编写的程序越来越大,类和对象就能很好地组织程序,减少代码量。
- 前言
- 第 1 章 计算与计算思维
- 1.1 什么是计算?
- 1.1.1 计算机与计算
- 1.1.2 计算机语言
- 1.1.3 算法
- 1.1.4 实现
- 1.2 什么是计算思维?
- 1.2.1 计算思维的基本原则
- 1.2.2 计算思维的具体例子
- 1.2.3 日常生活中的计算思维
- 1.2.4 计算思维对其他学科的影响
- 1.3 初识 Python
- 1.3.1 Python 简介
- 1.3.2 第一个程序
- 1.3.3 程序的执行方式
- 1.3.4 Python 语言的基本成分
- 1.4 程序排错
- 1.5 练习
- 第 2 章 用数据表示现实世界
- 2.1 数据和数据类型
- 2.1.1 数据是对现实的抽象
- 2.1.1 常量与变量
- 2.1.2 数据类型
- 2.1.3 Python 的动态类型*
- 2.2 数值类型
- 2.2.1 整数类型 int
- 2.2.2 长整数类型 long
- 2.2.3 浮点数类型 float
- 2.2.4 数学库模块 math
- 2.2.5 复数类型 complex*
- 2.3 字符串类型 str
- 2.3.1 字符串类型的字面值形式
- 2.3.2 字符串类型的操作
- 2.3.3 字符的机内表示
- 2.3.4 字符串类型与其他类型的转换
- 2.3.5 字符串库 string
- 2.4 布尔类型 bool
- 2.4.1 关系运算
- 2.4.2 逻辑运算
- 2.4.3 布尔代数运算定律*
- 2.4.4 Python 中真假的表示与计算*
- 2.5 列表和元组类型
- 2.5.1 列表类型 list
- 2.5.2 元组类型 tuple
- 2.6 数据的输入和输出
- 2.6.1 数据的输入
- 2.6.2 数据的输出
- 2.6.3 格式化输出
- 2.7 编程案例:查找问题
- 2.8 练习
- 第 3 章 数据处理的流程控制
- 3.1 顺序控制结构
- 3.2 分支控制结构
- 3.2.1 单分支结构
- 3.2.2 两路分支结构
- 3.2.3 多路分支结构
- 3.3 异常处理
- 3.3.1 传统的错误检测方法
- 3.3.2 传统错误检测方法的缺点
- 3.3.3 异常处理机制
- 3.4 循环控制结构
- 3.4.1 for 循环
- 3.4.2 while 循环
- 3.4.3 循环的非正常中断
- 3.4.4 嵌套循环
- 3.5 结构化程序设计
- 3.5.1 程序开发过程
- 3.5.2 结构化程序设计的基本内容
- 3.6 编程案例:如何求 n 个数据的最大值?
- 3.6.1 几种解题策略
- 3.6.2 经验总结
- 3.7 Python 布尔表达式用作控制结构*
- 3.8 练习
- 第 4 章 模块化编程
- 4.1 模块化编程基本概念
- 4.1.1 模块化设计概述
- 4.1.2 模块化编程
- 4.1.3 编程语言对模块化编程的支持
- 4.2 Python 语言中的函数
- 4.2.1 用函数减少重复代码 首先看一个简单的用字符画一棵树的程序:
- 4.2.2 用函数改善程序结构
- 4.2.3 用函数增强程序的通用性
- 4.2.4 小结:函数的定义与调用
- 4.2.5 变量的作用域
- 4.2.6 函数的返回值
- 4.3 自顶向下设计
- 4.3.1 顶层设计
- 4.3.2 第二层设计
- 4.3.3 第三层设计
- 4.3.4 第四层设计
- 4.3.5 自底向上实现与单元测试
- 4.3.6 开发过程小结
- 4.4 Python 模块*
- 4.4.1 模块的创建和使用
- 4.4.2 Python 程序架构
- 4.4.3 标准库模块
- 4.4.4 模块的有条件执行
- 4.5 练习
- 第 5 章 图形编程
- 5.1 概述
- 5.1.1 计算可视化
- 5.1.2 图形是复杂数据
- 5.1.3 用对象表示复杂数据
- 5.2 Tkinter 图形编程
- 5.2.1 导入模块及创建根窗口
- 5.2.2 创建画布
- 5.2.3 在画布上绘图
- 5.2.4 图形的事件处理
- 5.3 编程案例
- 5.3.1 统计图表
- 5.3.2 计算机动画
- 5.4 软件的层次化设计:一个案例
- 5.4.1 层次化体系结构
- 5.4.2 案例:图形库 graphics
- 5.4.3 graphics 与面向对象
- 5.5 练习
- 第 6 章 大量数据的表示和处理
- 6.1 概述
- 6.2 有序的数据集合体
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元组
- 6.3 无序的数据集合体
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 编程案例:文本文件分析
- 6.4.4 缓冲
- 6.4.5 二进制文件与随机存取*
- 6.5 几种高级数据结构*
- 6.5.1 链表
- 6.5.2 堆栈
- 6.5.3 队列
- 6.6 练习
- 第 7 章 面向对象思想与编程
- 7.1 数据与操作:两种观点
- 7.1.1 面向过程观点
- 7.1.2 面向对象观点
- 7.1.3 类是类型概念的发展
- 7.2 面向对象编程
- 7.2.1 类的定义
- 7.2.2 对象的创建
- 7.2.3 对象方法的调用
- 7.2.4 编程实例:模拟炮弹飞行
- 7.2.5 类与模块化
- 7.2.6 对象的集合体
- 7.3 超类与子类*
- 7.3.1 继承
- 7.3.2 覆写
- 7.3.3 多态性
- 7.4 面向对象设计*
- 7.5 练习
- 第 8 章 图形用户界面
- 8.1 图形用户界面概述
- 8.1.1 程序的用户界面
- 8.1.2 图形界面的组成
- 8.1.3 事件驱动
- 8.2 GUI 编程
- 8.2.1 UI 编程概述
- 8.2.2 初识 Tkinter
- 8.2.3 常见 GUI 构件的用法
- 8.2.4 布局
- 8.2.5 对话框*
- 8.3 Tkinter 事件驱动编程
- 8.3.1 事件和事件对象
- 8.3.2 事件处理
- 8.4 模型-视图设计方法
- 8.4.1 将 GUI 应用程序封装成对象
- 8.4.2 模型与视图
- 8.4.3 编程案例:汇率换算器
- 8.5 练习
- 第 9 章 模拟与并发
- 9.1 模拟
- 9.1.1 计算机建模
- 9.1.2 随机问题的建模与模拟
- 9.1.3 编程案例:乒乓球比赛模拟
- 9.2 原型法
- 9.3 并行计算*
- 9.3.1 串行、并发与并行
- 9.3.2 进程与线程
- 9.3.3 多线程编程的应用
- 9.3.4 Python 多线程编程
- 9.3.5 小结
- 9.4 练习
- 第 10 章 算法设计和分析
- 10.1 枚举法
- 10.2 递归
- 10.3 分治法
- 10.4 贪心法
- 10.5 算法分析
- 10.5.1 算法复杂度
- 10.5.2 算法分析实例
- 10.6 不可计算的问题
- 10.7 练习
- 第 11 章 计算+X
- 11.1 计算数学
- 11.2 生物信息学
- 11.3 计算物理学
- 11.4 计算化学
- 11.5 计算经济学
- 11.6 练习
- 附录
- 1 Python 异常处理参考
- 2 Tkinter 画布方法
- 3 Tkinter 编程参考
- 3.1 构件属性值的设置
- 3.2 构件的标准属性
- 3.3 各种构件的属性
- 3.4 对话框
- 3.5 事件
- 参考文献