UML简介5顺序(Sequence)图

本章我们将研究对象间行为的建模,即系统中对象之间的交互。

交互指定交互伙伴之间如何交换信息和数据。交互伙伴可以是人类(交互伙伴,如讲师或学生),也可以是非人类(如服务器、打印机或可执行软件)。交互可以是多人之间的对话,例如口试。或者,交互可以是HTTP等通信协议的模型,也可以是人类与软件系统之间的信息交换–例如,讲师公布考试成绩时讲师与学生管理系统之间的信息交换。交互也可以是程序或信号(如火警)中的一系列方法调用,以及由此产生的通信过程。

交互描述了多个交互伙伴之间的相互作用,由一系列消息组成。信息的发送或接收可以由某些事件的发生触发,例如收到另一条信息,也可以在指定的时间发生,例如05:00。预定义的约束条件指定了成功交互必须满足的任何必要前提条件。
例如,继续上述通信过程,讲师必须在输入学生成绩之前登录系统。

在 UML 中,您可以使用交互图来指定交互。在交互图交互图中,您总是模拟一个具体的场景,这意味着信息交换是在特定的上下文中进行的,以完成特定的任务。交互通常只描述一种情况的特定部分。通常还有其他有效的执行路径,而交互图并不涵盖这些路径。虽然通过消息交换的数据和交互伙伴处理或存储的数据可以在交互图中表示,但交互建模的目的并不是要明确说明如何操作这些数据。如果需要,您可以在交互图中添加这类信息,但其他图(如活动图)会优先对这类信息进行建模。

交互图为计算机专家、最终用户和决策者提供了一种描述不同详细程度通信序列的机制。因此,交互图可用于各种情况。例如,交互图用于表示一个完整系统与其环境之间的交互。在这种情况下,系统可以被理解为一个黑盒子,只有外部可见的界面才是已知的。您也可以使用交互图来模拟系统各部分之间的交互,以展示如何实现特定用例(参见第 3 章)。在后期设计阶段,您可以使用交互图对进程间通信进行精确建模,其中涉及的合作伙伴必须遵守某些协议。交互图还可以进一步放大待实现的系统,并在类级别上对通信进行建模,这意味着您可以使用交互图对操作调用和对象间行为进行建模。

在UML提供的四种交互图中,序列图是最常用的一种–通常以非正式的方式快速呈现交互序列。然而,在本章中,我们将详细描述序列图的元素,并研究如何根据UML标准应用它们。

5.1 交互伙伴

图片

在序列图中,交互伙伴被描绘成生命线。生命线 r:C 生命线是垂直线,通常是虚线,表示与之相关的对象的生命周期。生命线的上端是生命线的头部,是矩形,其中包含角色名称:类的表达式。该表达式表示与生命线相关联的角色名称和对象类别。与对象图相同,可以省略这两个名称中的一个。如果省略了类,则可以省略冒号;但是,如果只指定了类,则冒号必须在类名之前。因此,您可以在实例级和类级定义序列图。

图片

您也可以使用其他符号代替矩形来表示交互伙伴,例如我们在用例图中看到的用于角色的棒形图

在序列图中,角色概念的使用比简单的实例或类更能灵活建模。一个对象,即一个类的实例,可以在其生命周期中扮演不同的角色。在我们的 “角色”大学系统中,可以想象helen Lewis一开始只是一名学生,然后成为辅导员,最后成为教授。随着每一个新角色的出现,helen Lewis的某些活动就不再被允许或不再必须进行。然而,她现在却可以从事其他活动。

如果我们只考虑对象helen Lewis的类来反映该对象所能扮演的不同角色,那么每当该对象的角色发生变化时,我们就必须删除该对象并创建一个新的对象。或者,也可以动态更改类。

角色也可以连接到多个对象。这种角色被称为多值角色。但是,一条生命线只能代表一个特定对象。该对象由选择器选择。选择器是在角色名称和冒号之间的方括号中指定的。它可以用任何语言表述,例如自然语言、伪代码或 Java。在图(d) 的示例中,选择器只是一个充当索引的变量。要同时将一个角色的多个对象指定为独立的交互伙伴,必须将每个对象分配给单独的生命线。
图片

一条生命线可以代表一个活动对象。活动对象用于模型进程和线程。活动对象有自己的控制流,这意味着它可以独立于其他对象运行。表示活动对象的生命线头部左右各有一条双边界。通常使用连续条代替虚线(见图(e))。

在图(f)中,生命线的头部包含名称 self。当一个类跨越了某个交互上下文并参与了交互时,就需要这样做。

5.2 交换消息

序列图是二维图。横轴上显示的是参与交互的交互伙伴,它们应按清晰的顺序排列。纵轴表示交互的时间顺序。如果没有明确规定时间顺序,则纵轴上较高位置的事件发生在纵轴较低位置的事件之前,前提是这两个事件都涉及同一条生命线。
在序列图中,交互被视为一系列事件规范。事件规范包括消息的发送和接收 事件规范或基于时间的事件的发生。

图片

举例来说,时间轴的垂直轴决定了事件发生的顺序。垂直时间轴决定了一条生命线上事件发生的顺序,但这并不决定不同生命线上事件发生的顺序。只有在不同生命线之间交换信息时,才会强制规定多条生命线的顺序。除非另有说明,我们在下文中假定信息发送事件和接收事件的传输不需要任何时间,这意味着发送方的发送事件和接收方的接收事件是同时发生的。这使我们可以更紧凑地呈现跟踪结果,因为我们的跟踪不必考虑发送和接收事件的序列,而可以专注于报文序列。信息a和信息b之间的时间联系用符号→表示。

例如,a→b表示信息a在信息b之前发送。如果两条报文的发送和接收事件发生在同一条生命线上,那么这些事件的时间顺序就决定了报文的顺序。在下图(a)中,由于a的发送事件发生在c的发送事件之前,因此消息a必须总是发生在消息c之前。在图 (b)中,消息a和c 就是这种情况。因此有两种可能的轨迹a→c和c→a。

图片

如果在a和c之间插入信息 b,并且该信息迫使a和c 按时间顺序排列,则唯一可能的轨迹是a→b→c(见图(c))。

行为的执行由两个事件表示,这两个事件分别在同一条生命线上开始和结束执行。这种行为用条形图表示,称为执行规范。在直接执行的情况下,受影响的交互伙伴自己执行指定的行为;在间接执行的情况下,交互伙伴将执行委托给其他交互伙伴。重叠的执行规范用重叠条显示。如果交互伙伴向自己发送消息,并向自己发送信息,那么相关的执行规范将被明确建模,执行规范的两个条形图会重叠显示,其中消息箭头指向第二个条形图,如果是同步消息,执行结束时的虚线响应箭头会指向原来的条形图。

您不必为执行规范建模–它们是可选的,主要用于可视化交互伙伴执行某些行为的时间。许多UML工具会自动将相应的条形图绘制成连续的条形图,从影响交互伙伴的第一条信息到最后一条信息。为了清晰起见,在本书中我们一般不在序列图中显示执行规范。

5.3 消息

图片

在序列图中,消息被描绘成从消息发送方到接收方的箭头。箭头的类型表示所涉及的通信类型。同步报文用带有连续线段和填充三角形箭头的来表示。异步信息的箭头为连续的线和开口的箭头。在同步报文的情况下,发送方要等到收到回复报文后才能继续发送。响应报文用虚线表示,箭头开口。如果从上下文中可以清楚地看出响应报文的内容以及发送和接收响应报文的时间点,则可以省略图中的响应报文。在异步通信中,发送方在发送信息后继续通信。

图片

报文用名称标识,可指定参数和返回值。参数用逗号分隔,并用括号括起来。返回值也可以指定给一个变量。因此,可以用 var=m1:value 来标注报文,其中var是要分配返回值的变量,m1指定报文名称,value代表实际返回值。

图片

对象收到消息后,一般会调用类图中指定的相应操作。原则上,传递的参数应与类图中操作规范的参数一致。
但是,如果使用参数名称为相应参数赋值,参数的数量和顺序都不必与操作说明中的参数一致。

图片

用于创建对象的消息是一种特殊类型的消息。它的箭头是一个虚线箭头,箭头的末端是与要创建对象相关的生命线的头部。箭头上标有关键字new,相当于调用面向对象编程语言中的构造函数。

图片

如果对象在交互过程中被删除,即发生了销毁事件,则生命线的末端会标有大的 “销毁 事件 X”。否则,生命线会延伸到序列图的下端。

图片

如果消息的发送者未知或不相关,则可以将其与找到的消息一起按下。在这种情况下,您可以使用黑色圆圈作为消息来源,而不是指定发送消息的交互伙伴。

图片

与找到的信息相对应的是丢失的信息。对于这种丢失信息类型的信息,接收者是未知的或不相关的。接收者也会被标记为黑圈。

图片

到此为止,我们已经隐含地假定信息的传输没有任何时间损失。当然,这并不总是耗时信息的情况。如果要表示发送和接收报文之间的时间间隔,可以在序列图中将报文建模为一条对角线,而不是一条水平线。由于时间维度是垂直表示的,因此可视化信息传输所需的持续时间。这种类型的报文被称为耗时报文或有持续时间的报文。

图片

5.4 组合片段

在序列图中,您可以使用组合片段(操作符)来明确地对各种控制结构进行组合片段建模。这样就能紧凑而精确地描述多种可能的执行路径。在操作符图中,组合片段用矩形表示,矩形左上角的小五角星中的操作符类型由相应的关键字指定。UML提供 12 种不同类型的运算符。根据操作符的类型,它包含一个或多个操作数,而操作数又可以包含交互、组合操作数片段或对其他序列图的引用。操作符的不同操作数之间用水平虚线隔开。

  • 分支和循环
  • 并发和顺序
  • 过滤器和断言
图片

5.4.1 分支和循环

图片

Alt和opt片段示例。当学生要报名参加考试时,可能会出现以下情况 (1) 还有空位,学生可以报名。(2) 候补名单上有空位。学生必须决定是否进入候补名单。(3) 如果考试或候补名单上没有空位,学生会收到一条错误信息,并且不能注册该课程。

图片

关键字 loop 后面是循环迭代次数的可选说明。指定形式为 (min..max) 或 (min,max),其中 min 指定循环必须经历的最小迭代次数,max 表示最大迭代次数。如果 min 和 max 相同,可以省略其中一个数字和点。如果循环迭代次数没有上限,则只需指定星号 ∗。在这种情况下,最小迭代次数假定为零。如果关键字 loop 之后没有进一步指定迭代次数,则 ∗ 将被视为默认值。如果需要,可以指定一个 guard,然后在 (min,max) 限制内对每次迭代进行检查。这意味着,一旦迭代次数达到最小值,就会立即对保护进行评估。如果基本条件不满足,即使尚未达到最大执行次数,循环的执行也会终止。下图扩展了上图中的示例,包含了学生注册作业前所需的系统登录。正如循环参数所反映的,密码必须至少输入一次,最多输入三次。第一次尝试后,系统会检查密码是否可以被验证。如果可以,即密码不正确的条件不再为真,则停止执行循环中的交互操作。如果学生三次输入密码错误,系统也会退出循环。这种情况将在随后的中断片段中进一步处理。

图片

5.4.2 并发和顺序

如前所述,事件在纵轴上的排列代表这些事件的时间顺序,前提是相关的交互伙伴之间有消息交换。下面描述的组合片段允许您明确控制事件发生的顺序。

图片

seq片段代表默认顺序。它至少有一个seq片段seq具有弱排序操作数的顺序交互,表达了UML标准规定的弱排序,具体如下:

  • 每个操作数中事件的排序在结果中保持不变。
  • 来自不同操作数的不同生命线上的事件可以以任何顺序出现。
  • 来自不同操作数的同一生命线上的事件排序为:第一个操作数的事件先于第二个操作数的事件。

我们可以使用seq片段将信息分组,并将seq片段的断点应用与断点片段结合起来。如果断开片段的条件为真,则跳过seq片段中尚未执行的报文,在周围片段中继续执行序列图。下图显示了一个例子。一名学生想报名参加考试。如果所需的日期已没有空位,学生就会预订下一个日期(中断片段)。在这种情况下,讲师不会对学生进行考试,序列图的执行将在seq片段之外继续进行。无论注册是否成功,讲师都会向学生发送信息info()。

请注意,seq是默认顺序,通常无需显式建模。但在本例中,如果不对seq片段进行显式建模,如果密码不正确为真,执行就会在break片段之后结束。这是由于在执行了break片段的内容后,周围片段的操作会被省略。如果不使用seq,周围的片段就会成为图的最外层结构,因此整个执行过程就会结束。

图片

下图显示了另一个序列图和所有可能的轨迹。由于该图显示的是弱顺序,信息c与信息a和b在时间上没有关联,可以与这些信息交错在一起。由于b是由交互伙伴B发送的,而d也是由B接收的,因此这两条信息之间存在时间顺序。无论如何,e是最后一条信息。

图片

严格片段描述的是具有严格严格片段顺序的顺序交互。在不同操作数之间的不同生命线上,事件发生的顺序是重要的,这意味着即使交互伙伴之间没有信息交换,在纵轴上位置较高的操作数中的信息总是先于在纵轴上位置较低的操作数中的信息进行交换。

图片
图片

下图举例说明了 par 的使用。在课程开始时,讲师必须完成某些活动。讲师必须回答学生的询问、宣布考试日期和预订讲堂。为了完成这些工作,讲师必须与不同的人和系统进行通信。一个 par 片段用来表达完成这些活动的顺序并不重要。重要的是要遵守操作符内信息之间的默认顺序,也就是说,根据这个序列图,学生绝不会先注册课程,然后再向讲师发送查询。

图片

下图显示了并发交互的可能轨迹,但这次使用了par片段。来自不同操作数的报文之间不再有任何时间上的联系,这也是出现大量可能轨迹的原因。重要的是,操作数中的报文顺序必须得到遵守。

图片

另外,您也可以使用coregion来搁置单条生命线上事件的时间顺序。这样,您就可以为单条生命线的租用事件建模。在 coregion中,事件发生的顺序不会受到任何限制,即使它们是沿着生命线排列的。coregion所覆盖的生命线区域由旋转90度的方括号标记。

图片

为了确保交互的某些部分不会被意外事件打断,可以使用临界片段。这标记了交互作用中的临界片段临界原子交互作用原子区域。请注意,标准秩序seq适用于临界片段。

图片

下图只有紧接着信息c的信息d才是有效的踪迹。

图片

参考资料

  • 软件测试精品书籍文档下载持续更新 https://github.com/china-testing/python-testing-examples 请点赞,谢谢!
  • 本文涉及的python测试开发库 谢谢点赞! https://github.com/china-testing/python_cn_resouce
  • python精品书籍下载 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
  • Linux精品书籍下载 https://www.cnblogs.com/testing-/p/17438558.html

5.4.3 过滤器和断言

通常,序列图不会描述交互的所有方面。某些序列被突出显示并明确声明为允许的轨迹。然而,在大多数情况下,还可能出现更多允许但未描述的轨迹。在某些情况下,您必须记录所有可能出现的轨迹,或记录不得出现的轨迹。简而言之,序列图包含有效、无效和未指定的轨迹。过滤器和断言 “组中的组合片段定义了 (i) 可能出现但与系统描述无关的信息,(ii) 必须出现的信息,以及 (iii)哪些信息不能出现。遗憾的是,在 UML 标准中,对这一组片段的描述非常紧凑,这就是为什么在许多情况下,关于它们确切含义的许多问题仍然没有答案。下面,我们将对这些片段做一个简短的分解,并尽可能以标准为基础进行解释。
无关信息用忽ignore片段表示,表示这些信息可能在运行时出现,但对模型中描述的功能没有进一步的意义。忽略关键字后的大括号中注明了无关信息。

图片

相比之下,consider片段指定了对所考虑的交互特别重要的考虑片段相关交互的消息。这些信息也在关键字后以集合符号表示。所有出现在consider片段中,但未在相关信息集中指定的信息都会被自动归类为无关信息。它们必须被视为忽略片段的参数。

断言片段将某些建模跟踪确定为强制性的。不允许出现在现实中但未包含在图中的偏差。实际上,这意味着实现需要精确建模,而模型是一个完整的规范。
使用否定片段可以模拟无效交互,也就是说,否定片段无效交互描述的是不应该发生的情况。否定片段由一个操作数组成。您可以使用该片段明确强调经常出现的错误,并描述关键的错误序列。
但是,不应该/不必须发生的交互作用序列的数量是没有限制的,因此不能认为使用否定片段就能涵盖所有不希望发生的情况。

图片

5.5 更多语言元素

为了使我们能够更精确地指定交互并更清晰地描述交互,序列图提供了以下额外的语言元素,下面将详细介绍。交互引用和延续标记可以将序列图分解成模块,使其结构更加清晰。通过”Gates”可以模拟不同序列图或组合片段之间的信息流。参数和局部属性指定了执行交互所需的值。时间限制规定了某些事件必须发生的时间,而状态不变则规定了执行交互所必需的条件。

5.5.1 交互作用引用

交互作用引用允许将一个序列图引用集成到另一个序列图中。一方面,这允许您重复使用已建模的交互,另一方面,它使您能够将复杂的交互序列分解为模块,并以简单的形式进行描述。与组合片段一样,交互引用也是一个矩形,左上角有一个五边形。五边形包含关键字ref。

下图展示了交互引用的使用。登录学生管理系统的序列在序列图”登录”中建模,序列图”考试注册”引用了该序列图。

图片

5.5.2 Gates

原则上,报文不能超出交互、引用交互或组合片段的边界,即不能任意超出框架。为了使您能将消息交换扩展到这些边界之外,UML提供了门。因此,使用这种门可以发送和接收超出交互片段边界的消息门。门由消息箭头的顶端或末端可视化–取决消息是传入或传出的消息,触及表示序列图、交互参照或组合片段的框架边界。门可以用名称或使用门的消息名称来标识,也可以用消息的方向来标识。通过它们,可以为每条信息定义特定的发送方和接收方,即使发送方或接收方不在相应的交互或片段之外。您不必为组合片段明确加入门,这意味着消息可以直接指向接收者。

图片

5.5.3 Continuation Markers

Continuation Markers使您可以将另一个Continuation marker片段的操作数模块化。这样就可以将复杂的交互分解成若干部分,并用标记将它们相互连接起来。在这里,一个交互部分末尾的起始标记和目标标记指向另一个外部交互部分起始处的同名目标标记。

连续标记用圆角矩形表示,可以跨越多个交互伙伴。同名的连续标记必须指向相同的交互伙伴。

图片

5.5.4 参数和局部属性

序列图由一个矩形框包围,左上角有一个小五角星。这个五角星包含关键字sd,清楚地表明矩形框的内容是序列图。关键字 sd 后面是序列图的名称和可选参数,这些参数用逗号分隔,并用括号括起来。

图片

5.5.5 时间约束

时间约束指定事件发生的时间或两个事件之间的时间约束期。时间限制用大括号标注。
定时表达式代表具体的时间规范(例如定时表达式 {12:00})或计算规则(例如 {12:00+d})。可以使用关键字 at 指定绝对时间,例如 {at(12:00)}。
使用关键字after可以参照起始事件指定相对时间,例如 {after(5sec)}。在这两种情况下,定时表达式都用大括号表示。

图片

您还可以指定时间间隔。同样,时间间隔由 Interval 大括号括起来,并包含下限…上限形式的表达式。例如,要表示事件发生在 12:00 至 13:00 之间,可以使用 {12:00…13:00} 的形式。
关键字无

关键字 now 指定当前时间。它可以分配给任何属性,例如 t=now。当然,您也可以在任何时间限制中使用该属性,例如 {t…t+5}。
信息传输持续时间的计算用关键字duration表示。
您可以使用图中短水平线代表的定时标记为事件指定时间限制。如果一个时间限制指的是两个事件,也就是要定义两个事件之间的持续时间,则使用两个定时标记来指定

5.5.6 状态不变式

可以为交互指定状态不变式。状态不变式 状态不变式是指某个条件必须在某个时间满足。它总是分配给特定的生命线。在随后的事件发生之前,要对状态不变式是否为真进行评估。如果状态不变式不为真,则说明模型或实现不正确。状态不变式可以引用相关状态机图中的状态,也可以是引用本地属性的逻辑条件。
UML为状态不变式提供了三种符号选择:可以直接在生命线上用大括号指定状态不变式;可以将其作为注释附加;也可以将其放在生命线相应点的圆角矩形中。下图显示了这三种不同的符号。学生只能在以下情况下注册考试:(i) 学生已注册;

图片

5.6 创建序列图

5.6.1 类图和序列图之间的联系

不同的UML图不应被视为相互独立的;它们只是提供了对某一内容的不同看法。例如下面类图模拟了大学系统的一部分,其中还包括学生管理系统。

图片

学生管理系统可以直接访问所有学生和课程。系统知道存储在类Registration中的学生和课程注册数据。我们希望描述为某个学生创建某个课程的新注册所需的通信。为此,必须调用StudentAdminSystem类中的newRegistration方法。

要创建新注册,我们必须知道属于相应学籍号的学生对象和属于给定课程号的课程对象。我们可以通过调用getCourse和getStudent操作来获得这些信息。获得这些信息后,我们就可以创建一个注册类型的新对象,并调用init操作为注册对象设置学生和课程。

现在,我们只需在注册和课程之间建立联系,因为这两个方向的导航都是假定的。为此,我们需要调用setRegistration 方法。对于注册和学生对象,我们不需要这样做,因为从学生到注册的导航是不可能的。由此产生的序列图如图

图片

5.6.2 描述设计模式

序列图通常用于描述设计模式。设计模式为描述重复出现的问题提供了解决方案。下面我们将研究传输对象装配器模式,该模式描述了当分布式环境中的客户需要三个业务对象的信息时会发生的情况。

下图所示的解决方案中,客户机需要了解三个业务对象才能访问所需的数据。因此,客户端与业务对象之间的联系非常紧密–这通常是不可取的。我们可以使用传输对象装配器模式来打破这些依赖关系。在这种模式中,装配器将多个业务对象中的数据合并为一个传输对象,然后传输给客户端。这样,客户端就能以封装的形式接收到所需的数据。该模式的具体实现如下。

图片

5.7 通信、定时和交互概述图

除了序列图,UML 还支持另外三种类型的交互图:

  • 通信图
  • 定时图
  • 交互概览图

UML的四种交互图基于相同的基本元素,对于简单的交互一般是等价的。通过指定所涉及的通信伙伴和交换的信息,它们都描述了特定的通信情况。不过,每种图的重点不同。
在本节中,我们将通过示例对四种交互图进行简要比较。
下图以序列图的形式显示了登录过程。有三个交互伙伴:学生、电子学习系统和数据库。

图片

通信图是一种结构图,用于模拟通信伙伴之间的通信图关系。因此,它直接显示了谁与谁之间的通信。这些关系是信息交换的结果。在这里,时间不是一个单独的维度。报文交换的顺序使用十进制分类(顺序编号)来表示。

下图以交流图的形式显示了登录过程。同样,交互伙伴是学生、电子学习系统和数据库。从图中可以看出,学生与在线学习系统进行了两次通信:一次是使用 login(user, pw) ,另一次是使用 getCourses()。图中还显示,电子学习系统使用 check(user, pw) 与数据库通信。编号的顺序是登录、检查和获取课程。如箭头所示,所有三条信息都是同步信息;异步信息则像序列图一样用开放箭头表示。通信图中没有显示响应信息。

图片

Timing图显示事件发生时交互伙伴的状态变化。在Timing图中,交互伙伴和时间轴的排列正好相反,而在Timing图中,交互伙伴列在纵轴上,横轴表示时间顺序。生命线由一个完整的区域来描述,其中可以表示状态和状态转换。

图片

交互概览图显示了不同的交互 交互概览图直观地显示了交互发生的顺序和条件。
这样就可以将各种交互图按逻辑顺序排列。为此,我们主要使用活动图的概念。您可以指定整个交互图和交互引用作为节点,然后使用活动图的控制结构按顺序排列,而不是指定操作和对象的节点。实心圆代表初始节点,带圆环的实心圆代表最终节点。您可以使用空心菱形表示的决策节点来实现不同的路径。

图片

5.8 小结

序列图是UML中四种交互图之一。交互图为不同交互伙伴之间的通信建模,四个图中的每一个都侧重于不同的方面。在实践中,序列图是最常用的交互图。通信协议和设计模式的展示是序列图的突出应用,因为它们可以实现紧凑而清晰的规范。除了以生命线形式描述的交互伙伴外,序列图还包含不同类型的消息(同步、异步、响应消息、创建消息)。消息的时间顺序一般假定为沿垂直线从上到下。十二种组合片段为您提供了不同的控制结构,使您能够控制交互。下表概括了序列图中最重要的元素。

图片

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/344145.html

(0)
联系我们
联系我们
分享本页
返回顶部