08 【面向对象分析】
08 【面向对象分析】
在面向对象分析中,主要由对象模型、动态模型和功能模型组成。
面向对象分析(OOA)的关键是识别出问题域内的类与对象,并分析它们相互间的关系,最终建立起问题域的简洁、精确、可理解的正确模型。在用面向对象观点建立起的3种模型中,对象模型是最基本、最重要、最核心的。
1.面向对象分析的基本过程
1.1 概述
面向对象分析,就是抽取和整理用户需求并建立问题域精确模型的过程。
面向对象分析过程从分析陈述用户需求的文件开始。
接下来,系统分析员应该深入理解用户需求,抽象出目标系统的本质属性,并用模型准确地表示出来。
在面向对象建模的过程中,系统分析员必须认真向领域专家学习。
在面向对象建模的过程中,还应该仔细研究以前针对相同的或类似的问题域进行面向对象分析所得到的结果(可重用)。
1.2 3个模型与5个层次
在面向对象分析中,主要由对象模型、动态模型和功能模型组成。对象模型是最基本、最重要、最核心的。
面向对象建模得到的模型包含系统的3个要素,即静态结构(对象模型)、交互次序(动态模型)和数据变换(功能模型)。解决的问题不同,这3个子模型的重要程度也不同。
复杂问题(大型系统)的对象模型通常由下述5个层次组成:
![image-20230210224235300](https://article.biliimg.com/bfs/article/18fcb0490f0aef5a540b7bbb0bfb526809f01c6d.png)
现在再简要地介绍一下主题的概念。
主题是指导读者理解大型、复杂模型的一种机制。也就是说,通过划分主题把一个大型、复杂的对象模型分解成几个不同的概念范畴。
“读者”泛指所有需要读懂系统模型的人
![image-20230210224309857](https://article.biliimg.com/bfs/article/3107d454684fb8c8b6f9594b53f5d3032295cb17.png)
心理研究表明,人类的短期记忆能力一般限于一次记忆5~9个对象,这就是著名的7±2原则。面向对象分析从下述两个方面来体现这条原则:控制可见性和指导读者的注意力。
首先,面向对象分析通过控制读者能见到的层次数目来控制可见性。
其次,面向对象分析增加了一个主题层,它可以从一个相当高的层次描述总体模型,并对读者的注意力加以指导。
在概念上可以认为,面向对象分析大体上按照下列顺序进行:
寻找类与对象,识别结构,识别主题,定义属性,
建立动态模型,建立功能模型,定义服务。
但是,分析不可能严格地按照预定顺序进行,大型、复杂系统的模型需要反复构造多遍才能建成。
分析也不是一个机械的过程。系统分析员必须与领域专家及用户反复交流,以便澄清二义性,改正错误的概念,补足缺少的信息。
2.需求陈述
2.1 书写要点
需求陈述应该阐明“做什么”而不是“怎样做”!
应该描述用户的需求而不是提出解决问题的方法。
应该指出哪些是系统必要的性质,哪些是任选的性质。
应该避免对设计策略施加过多的约束,也不要描述系统的内部结构,因为这样做将限制实现的灵活性。
对系统性能及系统与外界环境交互协议进行描述。
对采用的软件工程标准、模块构造准则、将来可能做的扩充以及可维护性要求等方面进行描述。
书写需求陈述时,要尽力做到语法正确,而且应该慎重选用名词、动词、形容词和同义词。
系统分析员必须把需求与实现策略区分开。
应该看到,需求陈述仅仅是理解用户需求的出发点,它并不是一成不变的文档。
系统分析员必须与用户及领域专家密切配合协同工作,共同提炼和整理用户需求。
2.2 例子
![image-20230210224851359](https://article.biliimg.com/bfs/article/62216c02b26544733cfd5eae5eb2d46665381e54.png)
1.储户和柜员交互
银行柜员使用柜员终端处理储户提交的储蓄事务。
储户可以用现金或支票向自己拥有的某个账户内存款或开新账户,也可以从自己的账户中取款。
柜员负责把储户提交的存款或取款事务输进柜员终端。
柜员终端与相应的分行计算机通信,分行计算机具体处理针对某个账户的事务并且维护账户。
接收储户交来的现金或支票,或付给储户现金。
2.储户和ATM交互
首先,ATM要求用户输入密码。
接下来ATM把从这张卡上读到的信息以及用户输入的密码传给中央计算机,请求中央计算机核对这些信息并处理这次事务。
中央计算机根据卡上的分行代码确定这次事务与分行的对应关系,并且委托相应的分行计算机验证用户密码。
用户输入的密码是正确的,ATM就要求用户选择事务类型(取款、查询等)。当用户选择取款时,ATM请求用户输入取款额。
最后,ATM从现金出口吐出现金,并且打印出账单交给用户。
3.建立对象模型
面向对象分析首要的工作,是建立问题域的对象模型。
这个模型描述了现实世界中的“类与对象”以及它们之间的关系,表示了目标系统的静态数据结构。静态数据结构对应用细节依赖较少,比较容易确定。因此,用面向对象方法开发绝大多数软件时,都首先建立对象模型,然后再建立另外两个子模型。
需求陈述、应用领域的专业知识以及关于客观世界的常识,是建立对象模型时的主要信息来源。
对象模型通常有5个层次。典型的工作步骤是:
首先确定对象类和关联(因为它们影响系统整体结构和解决问题的方法),对于大型复杂问题还要进一步划分出若干个主题;
然后给类和关联增添属性,以进一步描述它们;
接下来利用适当的继承关系进一步合并和组织类。
而对类中操作的最后确定,则需等到建立了动态模型和功能模型之后,因为这两个子模型更准确地描述了对类中提供的服务的需求。
3.1 确定类与对象
类与对象是在问题域中客观存在的,系统分析员的主要任务就是通过分析找出这些类与对象。
首先找出所有候选的类与对象;
然后从候选的类与对象中筛选掉不正确的或不必要的。
1. 找出候选的类与对象
对象是对问题域中有意义的事物的抽象,它们既可能是物理实体,也可能是抽象概念。具体地说,大多数客观事物可分为下述5类。
(1) 可感知的物理实体,例如,飞机、汽车、书、房屋等。
(2) 人或组织的角色,例如,医生、教师、雇主、雇员、计算
机系、财务处等。
(3) 应该记忆的事件,例如,飞行、演出、访问、交通事故等。
(4) 两个或多个对象的相互作用,通常具有交易或接触的性质,
例如,购买、纳税、结婚等。
(5) 需要说明的概念,例如,政策、保险政策、版权法等。
另一种更简单的分析方法,是所谓的非正式分析。
用自然语言书写的需求陈述为依据,
把陈述中的名词作为类与对象的候选者,
用形容词作为确定属性的线索,
把动词作为服务(操作)的候选者。
当然,用这种简单方法确定的候选者是非常不准确的,其中往往包含大量不正确的或不必要的事物,还必须经过更进一步的严格筛选。通常,非正式分析是更详细、更精确的正式的面向对象分析的一个很好的开端。
2. 筛选出正确的类与对象
显然,仅通过一个简单、机械的过程不可能正确地完成分析工作。非正式分析仅仅帮助人们找到一些候选的类与对象,接下来应该严格考察每个候选对象,从中去掉不正确的或不必要的,仅保留确实应该记录其信息或需要其提供服务的那些对象。
筛选时主要依据下列标准,删除不正确或不必要的类与对象。
(1)冗余
如果两个类表达了同样的信息,则应该保留在此问题域中最富于描述力的名称。
例如ATM系统,其中储户与用户,现金兑换卡与磁卡及副本分别描述了相同的两类信息,因此,应该去掉“用户”、“磁卡”、“副本”等冗余的类,仅保留“储户”和“现金兑换卡”这两个类。
(2)无关
现实世界中存在许多对象,不能把它们都纳入到系统中去,仅需要把与本问题密切相关的类与对象放进目标系统中。有些类在其他问题中可能很重要,但与当前要解决的问题无关,同样也应该把它们删掉。
以ATM系统为例,这个系统并不处理分摊软件开发成本的问题,而且ATM和柜员终端放置的地点与本软件的关系也不大。因此,应该去掉候选类“成本”、“市”、“街道”、“营业厅”和“储蓄所”。
(3)笼统
在需求陈述中常常使用一些笼统的、泛指的名词,虽然在初步分析时把它们作为候选的类与对象列出来了,但是,要么系统无须记忆有关它们的信息,要么在需求陈述中有更明确更具体的名词对应它们所暗示的事务,因此,通常把这些笼统的或模糊的类去掉。
以ATM系统为例,“银行”实际指总行或分行,“访问”在这里实际指事务,“信息”的具体内容在需求陈述中随后就指明了。总之,在本例中应该去掉“银行”、“网络”、“系统”、“软件”、“信息”、“访问”等候选类。
(4)属性
在需求陈述中有些名词实际上描述的是其他对象的属性,应该把这些名词从候选类与对象中去掉。当然,如果某个性质具有很强的独立性,则应把它作为类而不是作为属性。
以ATM系统为例,“现金”、“支票”、“取款额”、“账单”、“余额”、“分行代码”、“卡号”、“密码”、“类型”等,实际上都应该作为属性对待。
(5)操作
在需求陈述中有时可能使用一些既可作为名词,又可作为动词的词,应该慎重考虑它们在本问题中的含义,以便正确地决定把它们作为类还是作为类中定义的操作。
例如,谈到电话时通常把“拨号”当作动词,当构造电话模型时确实应该把它作为一个操作,而不是一个类。但是,在开发电话的自动记账系统时,“拨号”需要有自己的属性(例如日期、时间、受话地点等),因此应该把它作为一个类。
(6)实现
在分析阶段不应该过早地考虑怎样实现目标系统。因此,应该去掉仅和实现有关的候选的类与对象。在设计和实现阶段,这些类与对象可能是重要的,但在分析阶段过早地考虑它们反而会分散人们的注意力。
在ATM系统的例子中,“事务日志”无非是对一系列事务的记录,它的确切表示方式是面向对象设计的议题;“通信链路”在逻辑上是一种联系,在系统实现时它是关联类的物理实现。总之,应该暂时去掉 “事务日志”和“通信链路”这两个类,在设计或实现时再考虑它们。
3.2 确定关联
两个或多个对象之间的相互依赖、相互作用的关系就是关联。
分析确定关联,能促使分析员考虑问题域的边缘情况,有助于发现那些尚未被发现的类与对象。
在分析确定关联的过程中,不必花过多的精力去区分关联和聚集。事实上,聚集不过是一种特殊的关联,是关联的一个特例。
1. 初步确定关联
在需求陈述中使用的描述性动词或动词词组,通常表示关联关系。
在初步确定关联时,大多数关联可以通过直接提取需求陈述中的动词词组而得出。
通过分析需求陈述,还能发现一些在陈述中隐含的关联。
最后,分析员还应该与用户及领域专家讨论问题域实体间的相互依赖、相互作用关系,根据领域知识再进一步补充一些关联。
2. 筛选
经初步分析得出的关联只能作为候选的关联,还需经过进一步筛选,以去掉不正确的或不必要的关联。
筛选时主要根据下述标准删除候选的关联。
(1) 已删去的类之间的关联
如果在分析确定类与对象的过程中已经删掉了某个候选类,则与这个类有关的关联也应该删去,或用其他类重新表达这个关联。
(2) 与问题无关的或应在实现阶段考虑的关联
应该把处在本问题域之外的关联或与实现密切相关的关联删去。
(3) 瞬时事件
关联应该描述问题域的静态结构,而不应该是一个瞬时事件。
(4) 三元关联
三个或三个以上对象之间的关联,大多可以分解为二元关联或用词组描述成限定的关联。
(5) 派生关联
应该去掉那些可以用其他关联定义的冗余关联。
3. 进一步完善
应该进一步完善经筛选后余下的关联。
通常从下述几个方面进行改进
(1) 正名
好的名字是帮助读者理解的关键因素之一。因此,应该仔细选择含义更明确的名字作为关联名。
(2) 分解
为了能够适用于不同的关联,必要时应该分解以前确定的类与对象。
(3) 补充
发现了遗漏的关联就应该及时补上。
(4) 表明重数
应该初步判定各个关联的类型,并粗略地确定关联的重数。但是,无须为此花费过多精力,因为在分析过程中随着认识的逐渐深入,重数也会经常改动。
3.3 划分主题
在开发大型、复杂系统的过程中,为了降低复杂程度,人们习惯于把系统再进一步划分成几个不同的主题,也就是在概念上把系统包含的内容分解成若干个范畴。
在开发很小的系统时,可能根本无须引入主题层;
对于含有较多对象的系统,则往往先识别出类与对象和关联,然后划分主题,并用它作为指导开发者和用户观察整个模型的一种机制;
对于规模极大的系统,则首先由高级分析员粗略地识别对象和关联,然后初步划分主题,经进一步分析,对系统结构有更深入的了解之后,再进一步修改和精炼主题。
应该按问题领域而不是用功能分解方法来确定主题。此外,应该按照使不同主题内的对象相互间依赖和交互最少的原则来确定主题。
3.4 确定属性
属性是对象的性质,藉助于属性人们能对类与对象和结构有更深入更具体的认识。注意,在分析阶段不要用属性来表示对象间的关系,使用关联能够表示两个对象间的任何关系,而且把关系表示得更清晰、更醒目。
一般说来,确定属性的过程包括分析和选择两个步骤。
1.分析
通常,在需求陈述中用名词词组表示属性,例如,“汽车的颜色”或“光标的位置”。
往往用形容词表示可枚举的具体属性,例如,“红色的”、“打开的”。
但是,不可能在需求陈述中找到所有属性,分析员还必须藉助于领域知识和常识才能分析得出需要的属性。幸运的是,属性对问题域的基本结构影响很小。随着时间的推移,问题域中的类始终保持稳定,属性却可能改变了,相应地,类中方法的复杂程度也将改变。
属性的确定既与问题域有关,也和目标系统的任务有关。应该仅考虑与具体应用直接相关的属性,不要考虑那些超出所要解决的问题范围的属性。在分析过程中应该首先找出最重要的属性,以后再逐渐把其余属性增添进去。在分析阶段不要考虑那些纯粹用于实现的属性。
2.选择
认真考察经初步分析而确定下来的那些属性,从中删掉不正确的或不必要的属性。通常有以下几种常见情况:
(1) 误把对象当作属性
如果某个实体的独立存在比它的值更重要,则应把它作为一个对象而不是对象的属性。在具体应用领域中具有自身性质的实体,必然是对象。同一个实体在不同应用领域中,到底应该作为对象还是属性,需要具体分析才能确定。例如,在邮政目录中,“城市”是一个属性,而在人口普查中却应该把“城市”当作对象。
(2) 误把关联类的属性当作一般对象的属性
如果某个性质依赖于某个关联链的存在,则该性质是关联类的属性,在分析阶段不应该把它作为一般对象的属性。特别是在多对多关联中,关联类属性很明显,即使在以后的开发阶段中,也不能把它归并成相互关联的两个对象中任一个的属性。
(3) 把限定误当成属性
正如9.4.2节所述,正确使用限定词往往可以减少关联的重数。如果把某个属性值固定下来以后能减少关联的重数,则应该考虑把这个属性重新表述成一个限定词。在ATM系统的例子中,“分行代码”、“账号”、“雇员号”、“站号”等都是限定词。
(4) 误把内部状态当成了属性
如果某个性质是对象的非公开的内部状态,则应该从对象模型中删掉这个属性。
(5) 过于细化
在分析阶段应该忽略那些对大多数操作都没有影响的属性。
(6) 存在不一致的属性
类应该是简单而且一致的。如果得出一些看起来与其他属性毫不相关的属性,则应该考虑把该类分解成两个不同的类。
3.5 识别继承关系
确定了类中应该定义的属性之后,就可以利用继承机制共享公共性质,并对系统中众多的类加以组织。正如以前曾经强调指出过的,继承关系的建立实质上是知识抽取过程,它应该反映出一定深度的领域知识,因此必须有领域专家密切配合才能完成。通常,许多归纳关系都是根据客观世界现有的分类模式建立起来的,只要可能,就应该使用现有的概念。
一般说来,可以使用两种方式建立继承(即泛化)关系。
(1) 自底向上:
抽象出现有类的共同性质泛化出父类,这个过程实质上模拟了人类归纳思维过程。
(2) 自顶向下:
把现有类细化成更具体的子类,这模拟了人类的演绎思维过程。从应用域中常常能明显看出应该做的自顶向下的具体化工作。
3.6 反复修改
仅仅经过一次建模过程很难得到完全正确的对象模型。事实上,软件开发过程就是一个多次反复修改、逐步完善的过程。在建模的任何一个步骤中,如果发现了模型的缺陷,都必须返回到前期阶段进行修改。
由于面向对象的概念和符号在整个开发过程中都是一致的,因此远比使用结构分析、设计技术更容易实现反复修改、逐步完善的过程。
实际上,有些细化工作(例如定义服务)是在建立了动态模型和功能模型之后才进行的。
4.建立动态模型
第一步,是编写典型交互行为的脚本。虽然脚本中不可能包括每个偶然事件,但是,至少必须保证不遗漏常见的交互行为。
第二步,从脚本中提取出事件,确定触发每个事件的动作对象以及接受事件的目标对象。
第三步,排列事件发生的次序,确定每个对象可能有的状态及状态间的转换关系,并用状态图描绘它们。
最后,比较各个对象的状态图,检查它们之间的一致性,确保事件之间的匹配。
4.1 编写脚本
在建立动态模型的过程中,脚本是指系统在某一执行期间内出现的一系列事件。脚本描述用户(或其他外部设备)与目标系统之间的一个或多个典型的交互过程(事件序列),以便对目标系统的行为有更具体的认识。
目的保证不遗漏重要的交互步骤,有助于确保整个交互过程的正确性和清晰性。
脚本描写的范围并不是固定的,既可以包括系统中发生的全部事件,也可以只包括由某些特定对象触发的事件。脚本描写的范围主要由编写脚本的具体目的决定。
即使在需求陈述中已经描写了完整的交互过程,也还需要花很大精力构思交互的形式。
因此,编写脚本的过程,实质上就是分析用户对系统交互行为的要求的过程。在编写脚本的过程中,需要与用户充分交换意见,编写后还应该经过他们审查与修改。
编写脚本时,
首先编写正常情况的脚本。
然后,考虑特殊情况,例如输入或输出的数据为最大值(或最小值)。
最后,考虑出错情况,例如,输入的值为非法值或响应失败。对大多数交互式系统来说,出错处理都是最难实现的部分。如果可能,应该允许用户“异常中止”一个操作或“取消”一个操作。
此外,还应该提供诸如“帮助”和状态查询之类的在基本交互行为之上的“通用”交互行为。
4.2 设想用户界面
大多数交互行为都可以分为应用逻辑和用户界面两部分。通常,系统分析员首先集中精力考虑系统的信息流和控制流,而不是首先考虑用户界面。
事实上,采用不同界面(例如命令行或图形用户界面),可以实现同样的程序逻辑。应用逻辑是内在的、本质的内容,用户界面是外在的表现形式。动态模型着重表示应用系统的控制逻辑。
但是,用户界面的美观程度、方便程度、易学程度以及效率等,是用户使用系统时最先感受到的,用户对系统的“第一印象”往往从界面得来,用户界面的好坏往往对用户是否喜欢、是否接受一个系统起很重要的作用。
在分析阶段不能完全忽略用户界面。在这个阶段用户界面的细节并不太重要,重要的是在这种界面下的信息交换方式。软件开发人员的目的是确保能够完成全部必要的信息交换,而不会丢失重要的信息。
不经过实际使用很难评价一个用户界面的优劣,因此,软件开发人员往往快速地建立起用户界面的原型,供用户试用与评价。
4.3 画事件跟踪图
完整、正确的脚本为建立动态模型奠定了必要的基础。但是,用自然语言书写的脚本往往不够简明,而且有时在阅读时会有二义性。为了有助于建立动态模型,通常在画状态图之前先画出事件跟踪图。为此首先需要进一步明确事件及事件与对象的关系。
1.确定事件
2.画出事件跟踪图
1. 确定事件
事件包括系统与用户(或外部设备)交互的所有信号、输入、输出、中断、动作等。从脚本中容易找出正常事件,但是,应该小心仔细,不要遗漏了异常事件和出错条件。
应该把对控制流产生相同效果的那些事件组合在一起作为一类事件,并给它们取一个唯一的名字。
但是,应该把对控制流有不同影响的那些事件区分开来,不要误把它们组合在一起。
一般说来,不同应用系统对相同事件的响应并不相同,因此,在最终分类所有事件之前,必须先画出状态图。如果从状态图中看出某些事件之间的差异对系统行为并没有影响,则可以忽略这些事件间的差异。
经过分析,应该区分出每类事件的发送对象和接受对象。一类事件相对它的发送对象来说是输出事件,但是相对它的接受对象来说则是输入事件。有时一个对象把事件发送给自己,在这种情况下,该事件既是输出事件又是输入事件。
2. 画出事件跟踪图
事件跟踪图实质上是扩充的脚本,可以认为事件跟踪图是简化的UML顺序图。
在事件跟踪图中,一条竖线代表一个对象,每个事件用一条水平的箭头线表示,箭头方向从事件的发送对象指向接受对象。时间从上向下递增,
画在最上面的水平箭头线代表最先发生的事件,画在最下面的水平箭头线所代表的事件最晚发生。箭头线之间的间距并没有具体含义,图中仅用箭头线在垂直方向上的相对位置表示事件发生的先后,并不表示两个事件之间的精确时间差。
4.4 画状态图
状态图描绘事件与对象状态的关系。当对象接受了一个事件以后,它的下个状态取决于当前状态及所接受的事件。由事件引起的状态改变称为“转换”。如果一个事件并不引起当前状态发生转换,则可忽略这个事件。
通常,用一张状态图描绘一类对象的行为,它确定了由事件序列引出的状态序列。
但是,也不是任何一个类都需要有一张状态图描绘它的行为。很多对象仅响应与过去历史无关的那些输入事件,或者把历史作为不影响控制流的参数。对于这类对象来说,状态图是不必要的。系统分析员应该集中精力仅考虑具有重要交互行为的那些类。
从一张事件跟踪图出发画状态图时,应该集中精力仅考虑影响一类对象的事件,也就是说,仅考虑事件跟踪图中指向某条竖线的那些箭头线。把这些事件作为状态图中的有向边(即箭头线),边上标以事件名。
两个事件之间的间隔就是一个状态。一般说来,如果同一个对象对相同事件的响应不同,则这个对象处在不同状态。应该尽量给每个状态取个有意义的名字。通常,从事件跟踪图中当前考虑的竖线射出的箭头线,是这条竖线代表的对象达到某个状态时所做的行为(往往是引起另一类对象状态转换的事件)。
根据一张事件跟踪图画出状态图之后,再把其他脚本的事件跟踪图合并到已画出的状态图中。为此需在事件跟踪图中找出以前考虑过的脚本的分支点(例如“验证账户”就是一个分支点,因为验证的结果可能是“账户有效”,也可能是“无效账户”),然后把其他脚本中的事件序列并入已有的状态图中,作为一条可选的路径。
考虑完正常事件之后再考虑边界情况和特殊情况
其中包括在不适当时候发生的事件(例如系统正在处理某个事务时,用户要求取消该事务)。有时用户(或外部设备)不能做出快速响应,然而某些资源又必须及时收回,于是在一定间隔后就产生了“超时”事件。对用户出错情况往往需要花费很多精力处理,并且会使原来清晰、紧凑的程序结构变得复杂、繁琐,但是,出错处理是不能省略的。
当状态图覆盖了所有脚本,包含了影响某类对象状态的全部事件时,该类的状态图就构造出来了。利用这张状态图可能会发现一些遗漏的情况。测试完整性和出错处理能力的最好方法,是设想各种可能出现的情况,多问几个“如果……,则……”的问题。
4.5 审查动态模型
各个类的状态图通过共享事件合并起来,构成了系统的动态模型。在完成了每个具有重要交互行为的类的状态图之后,应该检查系统级的完整性和一致性。
一般说来,每个事件都应该既有发送对象又有接受对象,当然,有时发送者和接受者是同一个对象。对于没有前驱或没有后继的状态应该着重审查,如果这个状态既不是交互序列的起点也不是终点,则发现了一个错误。
应该审查每个事件,跟踪它对系统中各个对象所产生的效果,以保证它们与每个脚本都匹配。
5.建立功能模型
功能模型表明了系统中数据之间的依赖关系,以及有关的数据处理功能,它由一组数据流图组成。其中的处理功能可以用IPO图(或表)、伪码等多种方式进一步描述。
通常在建立了对象模型和动态模型之后再建立功能模型。
5.1 画出基本系统模型图
基本系统模型由若干个数据源点/终点,及一个处理框组成,这个处理框代表了系统加工、变换数据的整体功能。
基本系统模型指明了目标系统的边界。由数据源点输入的数据和输出到数据终点的数据,是系统与外部世界之间的交互事件的参数。
5.2 画出功能级数据流图
把基本系统模型中单一的处理框分解成若干个处理框,以描述系统加工、变换数据的基本功能,就得到功能级数据流图。右图是ATM系统的功能级数据流图。
![image-20230212190702035](https://article.biliimg.com/bfs/article/0e083171985a467c70ae4d2d1e9ddd3b019ed690.png)
5.3 描述处理框功能
把数据流图分解细化到一定程度之后,就应该描述图中各个处理框的功能。应该注意的是,要着重描述每个处理框所代表的功能,而不是实现功能的具体算法。
描述既可以是说明性的,也可以是过程性的。
说明性描述规定了输入值和输出值之间的关系,以及输出值应遵循的规律。
过程性描述则通过算法说明“做什么”。
一般说来,说明性描述优于过程性描述,因为这类描述中通常不会隐含具体实现方面的考虑。
6.定义服务
“对象”是由描述其属性的数据,及可以对这些数据施加的操作(即服务),封装在一起构成的独立单元。因此,为建立完整的对象模型,既要确定类中应该定义的属性,又要确定类中应该定义的服务。
需要等到建立了动态模型和功能模型之后,才能最终确定类中应有的服务,因为这两个子模型更明确地描述了每个类中应该提供哪些服务。事实上,在确定类中应有的服务时,既要考虑该类实体的常规行为,又要考虑在本系统中特殊需要的服务。
1. 常规行为
在分析阶段可以认为,类中定义的每个属性都是可以访问的,也就是说,假设在每个类中都定义了读、写该类每个属性的操作。但是,通常无须在类图中显式表示这些常规操作。
2.从事件导出的操作
状态图中发往对象的事件也就是该对象接收到的消息,因此该对象必须有由消息选择符指定的操作,这个操作修改对象状态(即属性值)并启动相应的服务。
可以看出,所启动的这些服务通常就是接受事件的对象在相应状态的行为。
3. 与数据流图中处理框对应的操作
数据流图中的每个处理框都与一个对象(也可能是若干个对象)上的操作相对应。应该仔细对照状态图和数据流图,以便更正确地确定对象应该提供的服务。
4. 利用继承减少冗余操作
应该尽量利用继承机制以减少所需定义的服务数目。只要不违背领域知识和常识,就尽量抽取出相似类的公共属性和操作,以建立这些类的新父类,并在类等级的不同层次中正确地定义各个服务。
7.本章小结
面向对象分析中,主要由对象模型、动态模型和功能模型组成。
面向对象分析的关键工作,是分析、确定问题域中的对象及对象间的关系,并建立起问题域的对象模型。
大型、复杂系统的对象模型通常由下述5个层次组成:主题层、类与对象层、结构层、属性层和服务层。
分析模型是系统分析员同用户及领域专家交流时有效的通信手段。最终的模型必须得到用户和领域专家的确认。在交流和确认的过程中,原型往往能起很大的促进作用。