本节书摘来自异步社区《UML面向对象设计基础》一书中的第1章1.5节消息,作者【美】Meliir Page-Jones,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.5 消息
UML面向对象设计基础
对象通过消息请求另一个对象执行活动。许多消息还具有将信息从一个对象传送给另一个对象的作用。大多数老前辈都将消息列为重要的面向对象特性。
消息(message)是发送对象obj1向目标对象obj2发送请求的载体,申请对象obj2的一个方法。
本节对消息特性进行了剖析,描述了消息参数的特点,发送消息对象的角色,接收消息对象的角色及消息的三种类型。
1.5.1 消息结构
消息由几个含义组成,每个含义在面向对象设计中都十分重要。实际上,本书从头到尾会多次用到消息的特性。
对象obj1为给对象obj2发送一个显式消息,对象obj1必须知道三件事情:
① obj2的句柄。显然,你发送一个消息时,应该知道给谁发送。obj1通常将obj2的句柄保存在它的一个变量中。
② obj1希望执行的obj2操作名称。
③ obj2执行其操作时所要求的所有附加信息(参数)。
发送消息的对象(obj1)称为发送者,接收消息的对象(obj2)称为目标对象,也可以分别称为客户机(Client)和服务器(Server)。
机器人软件提供了几个消息的例子,其中一个为:
hom1.turnRight;
其中,hom1表示该消息(含有句柄)的目标对象。回想一下,赋值语句。
var hom1 :=Hominoid.New给hom1赋的句柄)。turnRight是目标对象执行操作的名称。这个消息不需要任何参数:turnRight总是旋转90°。
发送消息和调用传统的函数或过程有些类似。如用“准O.O.”语言,可以写出下面的语句:
call turnRight(hom1);
请注意这种倒置:使用传统的软件技术,我们先申请一个过程单元,然后向其提供操作的对象;在面向对象中,我们先申请一个对象,然后执行其中一个过程单元。
到此为止,这种区别似乎只停留在语法上,至多是观念上的。然而,当我在1.8节讨论多态性、重载及动态关联时,将体会到“先对象后过程”所引起的面向对象结构和传统结构之间的重要差别。因为不同的对象类可以使用相同的操作名称执行不同特定类的行为,或执行含义不同的类似行为。
1.5.2 消息参数
与传统的子程序一样,大多数消息都可以传入或传出参数。例如,如果名为 advance的操作返回一个标志,保存行进的输出,于是有下面的形式:
因此,给予目标对象的消息结构由激活目标操作的原形(signature)所定义。这个原形包括三个部分:操作名称,输入参数列表(前缀为 in),输出参数列表,也称返回参数(前缀为out)。每个参数列表都可以为空,在两个列表中可能出现相同的参数或只在前缀为inout的列表中出现一次,但在纯面向对象中很少出现这种情形。为简短起见,通常省略关键字in,将其作为缺省前缀。
消息的参数反映了面向对象软件和传统软件之间的另一个基本区别。在纯面向对象环境中(如Smalltalk),消息参数不是数据,而是对象句柄,因此,消息参数也当作对象。
例如,图1.10a以非正式的图形表示法说明了机器人程序中的一个消息:
hom1.advance(noOfSquares,out advanceOK)。
如果在执行消息时暂停hominoid程序的执行,取出消息参数的值,我们将发现意想不到的结果。如:
noOfSquare被置为123432
advanceOK 被置为664730
为什么会得到这些奇怪的数字?因为123432可能为对象(Integer类)的句柄,通常为整数2,664730可能为对象(Boolean类)的句柄,通常为逻辑值true,在面向对象环境中最好不用这些精确的数字,我使用这些数字只是用于说明。
如果你更喜欢图1.10b用UML表示方法来表示这个消息,第3章至第7章将会更深入地讨论这方面内容。
另外,再举一个例子,如果我们正执行一个面向对象的个人系统并做相同的事情,可能发现参数empOfMonth被置为441523。441523可能是对象(Employee类)的句柄表示Jim Spriggs先生。
1.5.3 消息中的对象角色
本节讨论面向对象系统中对象的四个角色。一个对象可以是:
消息的发送者。
消息的目标。
表示另一个对象中的变量(见1.4节)。
表示消息中传入和传出的参数(见1.5.2节)。
一个给定的对象在生存期可以扮演一个或多个角色。在图1.11中,我们可以看到所有这些角色。
从这个图1.11中,我们来看一下对象obj的一个操作op。这个操作发送消息给obj的三个变量指向的每一个对象。第一个消息只有一个输入参数;第二个消息只有一个输出参数;第三个消息既有输入参数也有输出参数。每个参数本身指向一个对象。这个结构非常典型地说明了对象操作如何与对象变量进行交互。
一些作者建议每个对象要么是“发送者”要么是“目标”。但实际不然,如图1.12所示。
对于message1,obj1是发送者,obj2是目标。对于message2,obj2是发送者,obj3是目标。因此我们看到,不同的时刻,同一个对象既可以是发送者又可以是目标。术语“发送者”和“目标”是相对给定消息而言的。它们不是对象本身固有的特性。
在纯面向对象环境中只有对象,每个对象扮演一个或前面提到的四个角色中的几个角色。在纯面向对象中不需要数据,因为对象可以完成数据完成的所有软件功能。在Smalltalk(非常纯的面向对象语言)中,确实没有任何数据!运行时只有指向其他对象的对象(通过变量),通过传递对象的句柄进行对象之间的通信。
但在C++(一种既面向数据或函数又面向对象的混合语言)中,参数可以表示任何信息。如果你的C++程序与Smalltalk一样纯,那么所有的参数都表示对象。但如果你将数据和对象混合在C++程序中,那么有一些参数就是简单的数据(或数据的指针)。对于Java语言也是如此,尽管Java语言远远不如C++那样随意。
1.5.4 消息的类型
对象可以接收三种类型的消息:报告消息,询问消息及祈使消息。本节对每一种消息进行举例说明。再一次借用机器人程序。在本书的最后第12章,讨论通信对象不同的设计方法时,再回到这些消息类型。
报告消息(informative message)是指向对象提供自我更新信息的消息(也称更新、向前或推出消息)。这是一种“面向过去”的消息,通常通知对象已经发生的事情。
报告消息的一个例子是employee.got(MarriageDate:Date)。这个消息告诉一个雇员对象某个雇员已经在某个日期结婚。通常,报告消息告诉一个对象由该对象表示的在现实世界中已经发生的事情。
询问消息(interrogative message)是请求一个对象显示自身一些信息的消息(也称为读、向后或回拉消息)。这是一种“面向现在”的消息,向对象询问当前信息。
询问消息的一个例子是hom1.location,向机器人询问当前所在的方格位置。这类消息实际上不改变任何事情,通常是向目标对象询问其表示的信息。
祈使消息(imperative message)请求对象对本身、另一个对象或系统环境执行某些操作(也称强制或动作消息)。这是一种“面向未来”的消息,请求对象执行将来的某些操作。
祈使消息的一个例子是hom1.advance,使机器人向前移动。这种消息通常使目标对象执行一些重要的算法。
类似地,假设向机器人发送下面的祈使消息:
hom1.goToLocation(square:Square,out feasible:Boolean)
该消息请求机器人只要可行,就走到特定的方格(机器人执行的计算量非常大)。
在实时的面向对象系统中,对象需要控制一些硬件,通常包含许多祈使消息。这些系统清楚地说明将要执行的祈使消息。下面是取自机器人世界的一个例子:
robotLeftHand.goToLocation(x,y,z:Length,theta1,theta2,theta3:Angle)
这个消息将使机器人的左手抬到某个位置并在空间定位。该算法可能要求机器人的手、机器人的胳臂或机器人本身移动。六个参数表示了手的六个*度,以三维空间表示。
下面让我们从消息转到面向对象公认的基本属性即对象类。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。