在二十世纪六十年代以前,软件系统都是较小且相对简单的;所用的编程语言都是十分简单(Fortran,Cobol等 );时兴个人英雄注意,即崇尚程序员的个人技能 ;代码是面条式的,特别是代码中含有GOTO语句。当时系统设计时常用的方法有功能分解法和数据流法。
以系统需要提供的功能为中心来组织系统。首先定义各种功能,然后把功能分解为子功能,同时定义功能之间的接口。对较大的子功能进一步分解,直到可给出明确的定义。根据功能/子功能的需要设计数据结构和算法。其优缺点如下:
优点:
缺点:
又称作结构化分析。基本策略是跟踪数据流,即研究问题域中数据如何流动以及在各个环节上进行何种处理,从而发现数据流和加工。问题域被映射为数据流图(DFD),并用处理说明和数据字典进行详细说明。其优缺点如下:
优点:
缺点:
上述的功能分解法和数据流发已经开发了很多软件系统。对于功能稳定的应用领域,如某些科学计算,上述方法是适用的。
对于众多的领域而言,其功能是易变的,如企业管理和商业管理领域就是如此。因为随着市场的变化,要对这些领域的管理模式不断地进行调整。对于较为复杂的系统,用上述方法进行软件开发,容易导致模块的低内聚和模块间的高耦合,从而使得系统缺乏灵活性和可维护性。
特别是由于当时团队的开发与管理方法的不足,使得在20世纪70年代的软件危机情况更加严重。
为了解决软件危机,人们对开发技术进行了一定的改进,对编程语言也进行了革新,如产生了用于软件开发的4GL、CASE工具、原型技术和代码生成器。这些努力取得了一定的成就,但没有从根本上解决问题。
面向对象方法的解决问题的思路是从现实世界中的客观对象(如人和事物)入手,尽量运用人类的自然思维方式来构造软件系统,这与传统的结构化方法从功能入手和信息工程化方法从信息入手是不一样的。在面向对象方法中,把一切都看成是对象。
从程序设计方法的角度看,面向对象是一种新的程序设计范型(paradigm),其基本思想是使用对象、类、继承、封装、聚合、关联、消息、多态性等基本概念来进行程序设计。是一种运用对象、类、继承、封装、聚合、关联、消息、多态性等概念来构造系统的软件开发方法。
过程抽象:任何一个完成确定功能的操作序列,其使用者都可把它看作一个单一的实体,尽管实际上它可能是由一系列更低级的操作完成的。
数据抽象:根据施加于数据之上的操作来定义数据类型,并限定数据的值只能由这些操作来修改和观察。
客观事物->对象->类->一般类
不同开发阶段需要进行不同程度的抽象,便于实现模块的可替换性
把对象的属性和操作结合成一个独立的系统单位,并尽可能隐蔽对象的内部细节。只是向外部提供接口,降低了对象间的耦合度。
封装的意义:
封装机制保证:数据不能被对象的使用者直接访问。只允许通过由对象提供的方法或代码访问数据。
借助消息传递,工作可从一个对象(客户)传递到另一个对象(代理),因为从客户的观点,代理具有客户所需要的操作。工作连续地传递,直到到达了既有数据又有方法(代码)能完成这项工作的对象。
注意:委托是执行任务的权利,而不是责任。
把具有共同性质的事物划分为一类,得出一个抽象的概念。分类帮助我们组织我们所生活的复杂世界。我们可以对在一个特殊分类中的对象做一些假设。如果一个对象是分类(类)的一个实例,它将符合该分类的总体模式。所有的对象都是类的实例。实例能够在运行时被产生(初始化)或销毁(删除)。对象怎样提供操作,由该对象为其实例的类所决定。这样,同一个类的所有对象在响应特定的操作请求(功能调用)时使用相同的方法
无多态性的泛化:类可以由层次继承结构所组织。在该结构中,子类将从位于层次结构高层的父类中继承属性、操作和关系。抽象的父类是指仅用来定义子类的超类。这样,抽象类就没有直接的实例。
有多态的泛化:可以使用层次继承结构组织类,子类可以继承位于层次结构的高层的父类的属性、操作和关系。然而,子类可以定义它自己的操作来代替其任何超类的同名操作。
即要求对象之间只能通过消息进行通讯。消息传递机制与函数调用机制的区别 :
关系机制为我们提供了用同等(关联、依赖)和层次(一般化/特殊化和聚合)结构组织类/对象的方法。很多面向对象的专家把模型的这部分结构称作静态模型。我们使用James Martin和James Odell的术语,将其称为结构分析。然而,一个应用/系统有了结构分析并不充分,还需要进行行为分析。行为分析是我们用来考察一个对象(类)是怎样提供它的操作的过程。 从分析的视点,有两种类型的行为:
静态的:在静态行为中,实现操作的代码不被任何外部或内部的事件(动作)所影响。
动态的:在行为中发生这些变化的原因可能是由于对象存在很多不同的状态。随后,对象根据它的状态做出反映。使用命令式编程技术不能很好地处理这种类型的行为。使用另外的一种称为有限状态机的机制会更好地捕获这样的方法。
引入包(package) 的概念,使模型具有大小不同的粒度层次,以利于控制复杂性。 如把分析和设计阶段的模型分别用包进行组织。
说明:书上=》抽象、分类、封装、消息通信、多态性、行为分析、复杂性控制
对象
对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组操作构成。
对象标识
对象标识就是对象的名字,有“外部标识”和“内部标识”之分。
类
类是具有相同属性和操作的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和操作务两个主要部分。类的作用是用来创建对象,对象是类的一个实例。
抽象
抽象(化)忽略事物的非本质特征,只注意那些与当前目标有关的本质特征,从而找出事物的共性。
分类
把具有共同性质的事物划分为一类,得出一个抽象的概念,叫做分类。
继承
特殊类拥有其一般类的全部属性与操作,称作特殊类对一般类的继承。继承意味着自动地拥有,或曰隐含地复制子类从父类中继承属性和操作,根据需要添加自己的属性和方法。继承简化了人们对事物的认识和描述,非常有益于软件复用,是OO技术提高软件开发效率的重要原因之一。一般类与特殊类之间的关系叫泛化关系(继承关系),简称泛化。例:
多态
多态是指同一个命名可具有不同的语义。OO方法中,常指在一般类中定义的属性或操作被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。
消息
对象通过它对外提供的操作在系统中发挥作用。当系统中的其他对象或其他系统成分(在不要求完全对象化的语言中,允许有不属于任何对象的成分,例如C++程序中的main函数),请求这个对象执行某个操作时,该对象就响应这个请求,完成该操作。在OO方法中,把向对象发出的操作请求称为消息。目前在大部分面向对象的编程语言中,消息其实就是函数(或过程)调用。但是,函数调用只是实现消息的方式之一,上述理解只适合于顺序系统。
聚合
一个(较复杂的)对象由其他若干(较简单的)对象作为其构成部分,称较复杂的对象为聚集,称较简单的对象为成分,称这种关系为聚合。如:
UML表示举例:
关联
类之间的静态联系称作关联。在实例化后,由类产生对象,由关联产生连接对象的链。
链是关联的实例。 关联的表示符号也称作实例连接:
与传统方法相比,面向对象方法的主要优点:
一是保证了对象行为的可靠性;
二是对它们的修改并不会影响其他的对象,有利于维护,对需求变化有较强的适应性。
把对象的属性和操作捆绑在一起,提高了对象(作为模块)的内聚性,减少了与其他对象的耦合,这为复用对象提供了可能性和方便性。在继承结构中,特殊类对一般类的继承,本身就是对一般类的属性和操作的复用。
面向对象的分析(Object Oriented Analysis, OOA),就是运用面向对象方法进行系统分析。其基本任务即运用面向对象方法,对问题域和系统责任进行分析和理解,找出描述问题域及系统责任所需的对象,定义对象的属性、操作以及它们之间的关系。其目标是建立一个符合问题域、满足用户需求的OOA模型。
OOA是分析,是软件生命周期的一个阶段,具有一般分析方法共同具有的内容、目标及策略;强调运用面向对象方法进行分析,用面向对象的概念和表示法表达分析结果。
问题域(problem domain):被开发系统的应用领域,即在现实世界中由这个系统进行处理的业务范围。
系统责任(system responsibilities):所开发的系统应该具备的职能。
从OOA到OOD不是转换,是调整和增补。使OOA作为OOD模型的问题域部分;增补其它四个部分,成为完整的OOD模型。
有不同的侧重点和不同的策略
OOA主要针对问题域,识别有关的对象以及它们之间的关系,产生一个映射问题域,满足用户需求,独立于实现的OOA模型。
OOD主要解决与实现有关的问题,基于OOA模型,针对具体的软、硬件条件(如机器、网络、OS、GUI、DBMS等)产生一个可实现的OOD模型。
人机交互、人机互动(Human–Computer Interaction或Human–Machine Interaction,简称HCI或HMI),是一门研究系统与用户之间的交互关系的是科学学科 。把人机交互部分作为系统中一个独立的组成部分,进行分析和设计,有利于隔离界面支持系统的变化对问题域部分的影响。人机交互使用的设备主要有键盘显示、鼠标、各种模式识别设备……
对使用系统的人进行分析
——以便设计出适合其特点的交互方式和界面表现形式;
对人和机器的交互过程进行分析
——核心问题是人如何命令系统,以及系统如何向人提交信息。
1.分析与系统交互的人——人员参与者
人对界面的需求,不仅在于人机交互的内容,而且在于他们对界面表现形式、风格等方面的爱好。前者是客观需求,对谁都一样;后者是主观需求,因人而异。
(1)列举所有的人员参与者
(2)调查研究
(3)区分人员类型
(4)统计(或估算)各类人员的比例
(5)了解使用者的主观需求
(6) 按照一定的准则进行折中与均衡
2.从use case分析人机交互
use case的构成
参与者的行为和系统行为按时序交替出现,左右分明。形成交叉排列的段落。
每个段落至少含有一个输入语句或输出语句;
有若干纯属参与者自身或系统自身的行为陈述;
可能包含一些控制语句或括号。
抽取方法:
删除所有与输入、输出无关的语句和不再包含任何内容的控制语句与括号,
剩下的就是对一个参与者(人)使用一项系统功能时的人机交互描述。
人机界面的设计准则:
1.命令的组织
不受欢迎的命令组织方式:
(1)一条命令含有大量的参数和任选项
(2)系统有大量命令,不加任何组织和引导
基本命令:使用一项独立的系统功能的命令。(提取后的用况)
命令步:在执行一条基本命令的交互过程中所包含的具体输入步骤。
高层命令:如果一条命令是在另一条命令的引导下被选用的,则后者称作前者的高层命令。
命令的组织措施——分解与组合
(1)分解:将一条含有许多参数和选项的命令分解为若干命令步
(2)组合:将基本命令组织成高层命令,从高层命令引向基本命令
基本命令及其命令步的结构
高层命令及其结构
按功能组织:如文件下有:创建、打开、关闭、打印、删除等。
按子系统组织:如文本编辑子系统、编译自系统。
两层命令之间的输出信息结构
在建立命令树时,应遵循如下策略:
控制流是进程(process)或线程(thread)的别称。有多个任务(控制流)并发执行的系统,称作多任务系统或并发系统。为了描述问题域固有的并发行为,表达实现所需的设计决策,需要在OOD部分对控制驱动部分进行建模。控制流驱动部分,用于定义和表示并发系统中的每个控制流。用主动对象表示每个控制流(进程、线程),所有的主动类构成控制流驱动部分。
在面向对象中,用一个主动对象表示一个独立的控制流,该对象驱动进程或线程,也即每个控制流都以一个表示独立的进程或线程的主动对象为根。 对控制驱动部分建模,通常,
1)OOA定义的主动对象,这是由业务逻辑所决定的
2)系统的并发需求所要求的多控制流,若要求多项工作同时进行,则每一项工作就是一个控制流。例如,销售与统计。
3)系统分布方案所要求的多控制流,每一个分布站点至少有一个控制流
4)根据任务的紧急程度设置控制流,高优先控制流,低优先控制流,紧急控制流;
5)为实现方便设立的控制流,例如:负责处理机之间通讯的控制流
6)对异常事件的处理,由于异常事件的发生,不能在程序的某个可预知的控制点进行处理,应该设立一个专门的控制流进行处理异常事件。
每个控制流应该有以上列举的理由之一,除非由明确的其他理由。
一个控制流中的对象调用另一个控制流中的对象的操作,是通过发送了一条同步消息来实现的。具体的执行步骤为:
(1)调用者调用操作
(2)调用者等待接收者接收这个调用
(3)接收者的操作被唤醒
(4)计算结果返回给调用者
(5)调用者继续它的执行。
一个控制流的对象异步地向另一个控制流中的对象发送一个信号。具体的执行步骤为:
(1)请求者发送信号,然后就继续它自己的执行;
(2)接收者只有在准备好时或在适当的时候,才到指定的邮箱去接收信号并进行处理,完成后可能向请求者发信号来回传处理结果,接着向下执行。
这样的邮箱机制也可以是同步的,但收发信号的双方事先要做好约定。
两个或几个控制流中的对象利用一块公共的存储器,作为通信区域。
通常传输具有较复杂和较大的数据结构的数据时,才使用共享存储器方式。
使用此方式,要注意同步问题。
用于在不同计算机中的并发进程:
(1)调用进程标示它想要请求的一个对象的操作,然后把它放在远程过程调用库中;
(2)远程过程调用机制在网络上寻找目标对象,找到后将请求打包发送给目标对象;
(3)目标方接到后将请求转换成本地格式,执行所请求的操作;执行完毕后,将结果以上述同样的方式返回给发送方。
数据管理部分是负责在特定的数据管理系统中存储和检索对象的组成部分。其目的是,存储问题域的持久对象、封装这些对象的查找和存储机制,以及为了隔离数据管理方案的影响。为了隔离数据管理系统对其它部分的影响,使得选用不同的数据管理系统时,问题域部分基本相同。
面向对象的数据库系统是OO设计和编程的之间扩展,是为了存储对象并与面向对象程序设计语言交互而专门设计的。它是按对象存储数据的数据库管理系统。面向对象数据库系统有两方面的特征:
(1)是面向对象的,应支持对象、类、操作、属性、继承、聚合、关联等面向对象的概念;
(2)另一方面它具有数据库系统所应具有的特定和功能。
当前的一种主流的做法是在面向对象的系统设计阶段的后期,考虑如何对系统的构件进行描述、构造和组织,以及构件如何在节点上进行分布。
1) 一个构件是系统的一个模块部分,而且是一个自包含的单元,它封装了其内部成分。
2) 构件通过它的提供接口和请求接口展现行为。
3) 构件是可替换的单元,在设计时和运行时依据接口的兼容性,若一个构件能提供与另一个构件所具有的功能,则前者可替换后者。
4) 构件起类型的作用,这意味着构件是可实例化的。
构件的表示法
接口(interface)接口由一组操作组成,它指定了一个契约,这个契约必须由实现和使用这个接口的构件的所遵循。除非用来表示常量,否则不需要属性。可以按各种约束(如前置和后置条件)的形式把一个接口与一个职责相关联,可以对通过这个接口的交互规定次序。接口分提供接口和请求接口。
把构件实现的接口称为提供接口,这意味着构件的提供接口是给其它构件提供服务的。实现接口的构件支持由该接口所拥有的特征,包括接口拥有的约束。
构件使用的接口被称为请求接口,即构件向其它构件请求服务时要遵循的接口。即构件向其它构件请求服务时要遵循的接口 。
一个构件可以实现多个接口,一个构件可以请求多个接口,一个接口可以由多个不同的构件实现。
接口对声明一个构件的总的行为来说是有用的,构件的实现仅需保证要实现在全部的提供接口中的操作。使用端口是为了能进一步控制这种实现。作为构件的一个部件,端口描述了在构件与它的环境之间以及在构件与它的内部部件之间的一个显式的交互点。也即,端口是一个封装构件的显式的对外窗口,所有进出构件的交互都要通过端口。这明确地指出,构件可以拥有内部结构和形式化其交互点的一组端口。构件的外部可见行为恰好是它端口的总和。
如果一个端口提供一个特定的接口而另一个端口需要这个接口,且接口是兼容的,那么这两个端口便是可连接的。连接件就是通过端口或接口用于构件实例间通讯的部件。
为了连接构件或把端口与构件内的部件相连,UML定义了两种连接件:委托连接件和装配连接件。
1. 装配连接件
是两个构件实例间的连接件,它定义一个构件实例提供服务,另一个构件实例使用这些服务。装配连接件用于把一个请求接口或端口与一个提供接口或端口的连接起来。在执行时,消息起源于一个请求端口,沿着连接件传递,被交付到一个提供端口
2.委托连接件
把外部对构件端口的请求分发到构件内部的部件实例进行处理,或者通过构件端口把构件内部部件实例向构件外部的请求分发出去。
委托有这样的含义:
具体的消息流将发生在所连接的端口之间,可能要跨越多个层次,最终到达要对消息进行处理的最终部件实例。这样,使用委托连接件可对构件行为的层次分解建模。