我来补这学期欠下的坑了,比起博客园,里面的文章会更侧重于关于做题的思路,因此有可能直接以最后一次题目来分析了(毕竟迭代开发),有关面向对象相关的内容,比如类、对象、方法、属性等等或许相对少这一单元来说,这一单元就不讲架构的迭代之类的吧,多介绍自己心路分析过程这种的
UML 解析
题目大意
预备知识
传入参数与返回值
UML 中操作参数元素 UMLParameter
的 direction
属性决定了它是传入参数还是返回值:
in
:传入参数return
:返回值
在所有的数据中,direction
只会有上述两种类型。
NamedType
对于属性和操作中的传入参数而言,NamedType
包括
- Java 语言的八大基本类型(
byte
/short
/int
/long
/float
/double
/char
/boolean
) String
其余一律视为错误类型。
对于操作中的返回值而言,NamedType
包括
- Java 语言的八大基本类型(
byte
/short
/int
/long
/float
/double
/char
/boolean
) String
void
(实际上,void
也算是一种类型,C/C++/Java 对于这件事也都是这样的定义,void
不等同于无返回值)
其余一律视为错误类型。
ReferenceType
对于属性和操作中的传入参数、返回值而言,ReferenceType
指向已定义的类或接口,类型名为对应的类名或接口名。在本单元中,我们保证它是合法的,即 ReferenceType
不可能为错误类型。
类型相同
当两个类型对应的 NameableType
对象(参见官方包中的相关接口)在 Objects.equals()
下为真时,则这两个类型相同。
参数列表相同
对于任意两个操作,若它们具有相同数量的传入参数,且这两组传入参数之间存在某一一映射使得相对应的参数类型相同,则这两个操作的参数列表相同。例如 int op(int, Class1, double)
和 String op(double, int, Class1)
的参数列表相同。
重复操作
对于任意两个操作,如果它们的名称相同,且操作的参数列表相同,则它们互为重复操作。
耦合度
OO 度量指标中的类耦合度(Coupling Between Objects, CBO)最早由 Chidamber 与 Kemerer 于 1994 年提出,并被后人不断完善。在这里,我们采用 Visual Studio 2019 中 类耦合度指标 的约定,给出本单元中操作的耦合度与属性的耦合度的相关计算规则。
操作的耦合度:对于一个类的某个操作,考虑其所有类型为 ReferenceType
的操作参数类型(即元素 UMLParameter
的 type
属性,包括传入参数与返回值):
- 若
ReferenceType
引用的类或接口是当前查询的类,则耦合度不增加,否则耦合度增加 $1$; - 若存在多个
ReferenceType
引用的类或接口相同,则耦合度只会被计算一次,不会重复计算。
属性的耦合度:对于一个类,考虑其所有类型为 ReferenceType
的属性类型(即元素 UMLAttribute
的 type
属性):
- 若
ReferenceType
引用的类或接口是当前查询的类,则耦合度不增加,否则耦合度增加 $1$; - 若存在多个
ReferenceType
引用的类或接口相同,则耦合度只会被计算一次,不会重复计算。
继承深度
对于类 X,若 Y 满足:
- X 是 Y 的子类(此处特别定义,X 也是 X 的子类);
- 不存在类 Z,使得 Z 是 Y 的父类;
则称 Y 是 X 的顶级父类。这里的继承关系只考虑 UML 中存在的 UmlGeneralization
定义的继承关系,而不考虑所有类都默认继承自 Object
这个情况。
定义继承深度为某类到其顶级父类之间,经过的直接继承次数。例如:若只有两个类 A 与 B,且 A 直接继承 B 时,A 的继承深度为 $1$;若只有一个类 A ,则 A 的继承深度为 $0$。
关键状态
- 删除某个状态之后无法从 Initial State 到达任意一个 Final State,且不与后两条规定相悖,则该状态是关键状态;
- 如果状态机模型本来就无法从 Initial State 到达任意一个 Final State(包括 Final State 不存在的情况),则该状态机中所有状态都不是关键状态;
- Initial State 与 Final State 不是关键状态。
某字段为空
指该字段是空指针 null
,或仅包含空白字符(空格和制表符 \t
)。
题目要求
一、作业要求
本次作业的程序主干逻辑(包括解析 .mdj
格式的文件为关键数据)均已实现,只需要同学们完成剩下的部分,即:通过实现官方提供的接口,来实现自己的 UML 分析器。
官方的接口定义源代码都已在接口源代码文件中给出,各位同学需要实现相应的官方接口,并保证代码实现功能正确。
具体来说,各位同学需要新建一个类,并实现相应的接口方法。
当然,还需要同学们在主类中调用官方包的 AppRunner
类,并载入自己实现的 UML 解析器类,来使得程序完整可运行,具体形式参考本次作业官方包目录下的 README.md
。
代码结构说明
UserApi
的具体接口规格见官方包的 jar 文件,此处不加赘述。
除此之外,UserApi
类必须实现一个构造方法
1
2
3
public class MyImplementation implements UserApi {
public MyImplementation(UmlElement[] elements);
}
或者
1
2
3
public class MyImplementation implements UserApi {
public MyImplementation(UmlElement... elements);
}
构造函数的逻辑为将 elements
数组内的各个 UML 元素传入 MyImplementation
类,以备后续解析。
请确保构造函数正确实现,且类和构造函数均定义为 public。AppRunner
内将自动获取此构造函数进行 UserApi
实例的生成。
(注:这两个构造方法实际本质上等价,不过后者实际测试使用时的体验会更好,想要了解更多的话可以百度一下,关键词:Java 变长参数
)
交互模式
- 调用上述构造函数,生成一个实例,并将 UML 模型元素传入;
- 之后将调用此实例的各个接口方法,以实现基于之前传入的 UML 模型元素的各类查询操作;
- 官方接口通过调用方法的返回值,自动生成对应的输出文本。
二、关于类图的查询指令
该部分内容与上次作业完全相同。
各个指令对应的方法名和参数的表示方法详见官方接口说明。
指令 1:模型中一共有多少个类
输入指令格式:CLASS_COUNT
举例:CLASS_COUNT
输出:
Total class count is x.
- 其中
x
为模型中类的总数。
- 其中
指令 2:类的子类数量
输入指令格式:CLASS_SUBCLASS_COUNT classname
举例:CLASS_SUBCLASS_COUNT Elevator
输出:
Ok, subclass count of class "classname" is x.
- 其中
x
为直接继承类classname
的子类数量;
- 其中
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容。
- 存在多个名为
指令 3:类中的操作有多少个
输入指令格式:CLASS_OPERATION_COUNT classname
举例:CLASS_OPERATION_COUNT Elevator
输出:
Ok, operation count of class "classname" is x.
- 其中
x
为类classname
中的操作个数;
- 其中
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容。
- 存在多个名为
说明:
- 本指令中统计的一律为此类自己定义的操作,不包含继承自其各级父类所定义的操作;
- 本指令中统计的无需考虑重复操作带来的影响。若有多个操作为重复操作,则这些操作都需要分别计入答案。
指令 4:类的操作可见性
输入指令格式:CLASS_OPERATION_VISIBILITY classname methodname
举例:CLASS_OPERATION_VISIBILITY Taxi setStatus
输出:
Ok, operation visibility of method "methodname" in class "classname" is public: www, protected: xxx, private: yyy, package-private: zzz.
- 其中
www
/xxx
/yyy
/zzz
分别表示类classname
中,名为methodname
且实际可见性分别为public
/protected
/private
/package-private
的操作个数; - 如果类中不存在名为
methodname
的操作,则www
/xxx
/yyy
/zzz
全部设置为 0;
- 其中
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容。
- 存在多个名为
说明:
- 本指令中统计的一律为此类自己定义的操作,不包含继承自其各级父类所定义的操作;
- 在上一条的前提下,需要统计出全部的名为
methodname
的操作的可见性信息。 - 本指令中统计的无需考虑重复操作带来的影响。若有多个操作为重复操作,则这些操作都需要分别计入答案。
指令 5:类的操作的耦合度
输入指令格式:CLASS_OPERATION_COUPLING_DEGREE classname methodname
举例:CLASS_OPERATION_COUPLING_DEGREE Taxi setStatus
输出:
Ok, method "methodname" in class "classname" has coupling degree: coupling_degree_1, coupling_degree_2, coupling_degree_3.
- 此例中,类中名为
methodname
的操作共有 3 个,它们的操作的耦合度分别为coupling_degree_1
、coupling_degree_2
、coupling_degree_3
,且这些操作中不存在重复操作; - 传出列表时可以乱序,官方接口会自动进行排序(但是需要编写者自行保证不重不漏);
- 如果类中不存在名为
methodname
的操作,则传出一个空列表;
- 此例中,类中名为
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容;
- 存在多个名为
Failed, wrong type of parameters or return value in method "methodname" of class "classname".
- 类
classname
中所有名为methodname
的操作中,存在某个操作具有错误类型时,输出上述内容;
- 类
Failed, duplicated method "methodname" in class "classname".
- 类
classname
中所有名为methodname
的操作存在重复操作时,输出上述内容。
- 类
说明:
- 本指令中统计的一律为此类自己定义的操作,不包含继承自其各级父类所定义的操作;
- 如果同时存在错误类型和重复操作两种异常,按错误类型异常处理。
指令 6:类的属性的耦合度
输入指令格式:CLASS_ATTR_COUPLING_DEGREE classname
举例:CLASS_ATTR_COUPLING_DEGREE Taxi
输出:
Ok, attributes in class "classname" has coupling degree x.
- 其中
x
为类classname
的属性的耦合度
- 其中
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容。
- 存在多个名为
说明:
- 本指令的查询需要考虑继承自其各级父类所定义的属性,但不需要考虑实现的接口所定义的属性(无论是直接实现还是通过父类或者接口继承等方式间接实现,都算做实现了接口);
- 本查询中忽略属性名称相同的错误。
指令 7:类实现的全部接口
输入指令格式:CLASS_IMPLEMENT_INTERFACE_LIST classname
举例:CLASS_IMPLEMENT_INTERFACE_LIST Taxi
输出:
Ok, implement interfaces of class "classname" are (A, B, C).
- 此例中,类
classname
实现了A
、B
、C
这 3 个接口; - 无论是直接实现还是通过父类或者接口继承等方式间接实现,都算做实现了接口;
- 传出列表时可以乱序,官方接口会自动进行排序(但是需要编写者自行保证不重不漏);
- 如果类
classname
没有实现任何接口,则传出一个空列表;
- 此例中,类
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容。
- 存在多个名为
指令 8:类的继承深度
输入指令格式:CLASS_DEPTH_OF_INHERITANCE classname
举例:CLASS_DEPTH_OF_INHERITANCE AdvancedTaxi
输出:
Ok, depth of inheritance of class "classname" is x.
- 其中
x
为类classname
的继承深度;
- 其中
Failed, class "classname" not found.
- 不存在名为
classname
的类时,输出上述内容;
- 不存在名为
Failed, duplicated class "classname".
- 存在多个名为
classname
的类时,输出上述内容。
- 存在多个名为
三、关于 UML 状态图的查询指令
除了如下改动:
- 用某字段为空的定义等价替换了原有的定义(含义未改变,仅为了统一说明)
其余部分内容与上次作业完全相同。
指令 1:给定状态机模型中一共有多少个状态
输入指令格式:STATE_COUNT statemachine_name
举例:STATE_COUNT complex_sm
输出:
Ok,state count of statemachine "statemachine_name" is x.
- 其中
x
为状态机模型statemachine_name
(UMLStateMachine)中的状态个数;
- 其中
Failed, statemachine "statemachine_name" not found.
- 不存在名为
statemachine_name
的状态机模型时,输出上述内容;
- 不存在名为
Failed, duplicated statemachine "statemachine_name".
- 存在多个名为
statemachine_name
的状态机模型时,输出上述内容。
- 存在多个名为
说明:
- Initial State 和 Final State 均算作状态。
指令 2:给定状态机模型和其中的一个状态,判断其是否是关键状态
输入指令格式:STATE_IS_CRITICAL_POINT statemachine_name statename
举例:STATE_IS_CRITICAL_POINT complex_sm open
输出:
Ok, state "statename" in statemachine "statemachine_name" is a critical point.
- 状态机模型
statemachine_name
中的statename
状态是关键状态时,输出上述内容;
- 状态机模型
Ok, state "statename" in statemachine "statemachine_name" is not a critical point.
- 状态机模型
statemachine_name
中的statename
状态不是关键状态时,输出上述内容;
- 状态机模型
Failed, statemachine "statemachine_name" not found.
- 不存在名为
statemachine_name
的状态机模型时,输出上述内容;
- 不存在名为
Failed, duplicated statemachine "statemachine_name".
- 存在多个名为
statemachine_name
的状态机模型时,输出上述内容;
- 存在多个名为
Failed, state "statename" in statemachine "statemachine_name" not found.
- 状态机模型
statemachine_name
中不存在名为statename
的状态时,输出上述内容;
- 状态机模型
Failed, duplicated state "statename" in statemachine "statemachine_name".
- 状态机模型
statemachine_name
中存在多个名为statename
的状态时,输出上述内容。
- 状态机模型
指令 3:给定状态机模型和其中两个状态,引起状态迁移的所有触发事件
输入指令格式:TRANSITION_TRIGGER statemachine_name statename1 statename2
举例:TRANSITION_TRIGGER door_sm open close
输出:
Ok,triggers of transition from state "statename1" to state "statename2" in statemachine "statemachine_name" are (A, B, C).
- 此例中,引起状态机模型
statemachine_name
从statename1
迁移到statename2
的事件共有 3 个,分别为A
、B
、C
; - 传出列表时可以乱序,官方接口会自动进行排序(但是需要编写者自行保证不重不漏);
- 此例中,引起状态机模型
Failed, statemachine "statemachine_name" not found.
- 不存在名为
statemachine_name
的状态机模型时,输出上述内容;
- 不存在名为
Failed, duplicated statemachine "statemachine_name".
- 存在多个名为
statemachine_name
的状态机模型时,输出上述内容;
- 存在多个名为
Failed, state "statename1" in statemachine "statemachine_name" not found.
- 状态机模型
statemachine_name
中不存在名为statename1
的状态时,输出上述内容;
- 状态机模型
Failed, duplicated state "statename1" in statemachine "statemachine_name".
- 状态机模型
statemachine_name
中存在多个名为statename1
的状态时,输出上述内容;
- 状态机模型
Failed, transition from state "statename1" to state "statename2" in statemachine "statemachine_name" not found.
- 状态机模型
statemachine_name
中未找到任何从状态statename1
到状态statename2
的迁移时,输出上述内容。
- 状态机模型
说明:
- 该询问考虑的迁移为状态间的直接迁移;
- 检测状态与迁移异常时,先检测起点状态是否存在异常,再检测终点状态是否存在异常,最后检查是否存在相应的迁移;
- 任意两个状态间有零个或多个迁移;
- 我们保证对于每个非 Initial State 到非 Initial State 的迁移,都至少有一个触发事件;
- 一个迁移上的所有触发事件名称两两不同,且不为空。
四、关于 UML 顺序图的查询指令
该部分内容与上次作业完全相同。
指令 1:给定 UML 顺序图,一共有多少个参与对象
输入指令格式:PTCP_OBJ_COUNT umlinteraction_name
举例:PTCP_OBJ_COUNT normal
输出:
- ` Ok, participant count of umlinteraction “umlinteraction_name” is x.`
- 其中
x
为顺序图模型umlinteraction_name
(UMLInteraction)中的参与对象(UMLLifeline)个数;
- 其中
Failed, umlinteraction "umlinteraction_name" not found.
- 不存在名为
umlinteraction_name
的顺序图模型时,输出上述内容;
- 不存在名为
Failed, duplicated umlinteraction "umlinteraction_name".
- 存在多个名为
umlinteraction_name
的顺序图模型时,输出上述内容。
- 存在多个名为
指令 2:给定 UML 顺序图和参与对象,找出能创建该参与对象的另一个参与对象
输入指令格式:PTCP_CREATOR umlinteraction_name lifeline_name
举例:PTCP_CREATOR normal door
输出:
Ok, lifeline "lifeline_name" in umlinteraction "umlinteraction_name" can be created by "creator_name".
- 其中
creator_name
为顺序图模型umlinteraction_name
中能创建lifeline_name
的参与对象;
- 其中
Failed, umlinteraction "umlinteraction_name" not found.
- 不存在名为
umlinteraction_name
的顺序图模型时,输出上述内容;
- 不存在名为
Failed, duplicated umlinteraction "umlinteraction_name".
- 存在多个名为
umlinteraction_name
的顺序图模型时,输出上述内容。
- 存在多个名为
Failed, lifeline "lifeline_name" in umlinteraction "umlinteraction_name" not found.
- 顺序图模型
umlinteraction_name
中不存在名为lifeline_name
的参与对象时,输出上述内容;
- 顺序图模型
Failed, duplicated lifeline "lifeline_name" in umlinteraction "umlinteraction_name".
- 顺序图模型
umlinteraction_name
中存在多个名为lifeline_name
的参与对象时,输出上述内容;
- 顺序图模型
Failed, lifeline "lifeline_name" in umlinteraction "umlinteraction_name" is never created.
- 顺序图模型
umlinteraction_name
中的参与对象lifeline_name
没有收到创建消息时,输出上述内容;
- 顺序图模型
Failed, lifeline "lifeline_name" in umlinteraction "umlinteraction_name" is created repeatedly.
- 顺序图模型
umlinteraction_name
中的参与对象lifeline_name
收到多条创建消息时,输出上述内容。
- 顺序图模型
说明:
- 测试数据中不会出现 Endpoint 向 Lifeline 发送 Create Message 的情况。
指令 3:给定 UML 顺序图和参与对象,收到了多少个 Found 消息,发出了多少个 Lost 消息。
输入指令格式:PTCP_LOST_AND_FOUND umlinteraction_name lifeline_name
举例:PTCP_LOST_AND_FOUND normal door
输出:
Ok, incoming found message and outgoing lost message count of lifeline "lifeline_name" of umlinteraction "umlinteraction_name" is x and y.
- 其中
x
为顺序图模型umlinteraction_name
(UMLInteraction)中lifeline_name
收到的 Found 的消息个数,y
为发送的 Lost 消息个数;
- 其中
Failed, umlinteraction "umlinteraction_name" not found.
- 不存在名为
umlinteraction_name
的顺序图模型时,输出上述内容;
- 不存在名为
Failed, duplicated umlinteraction "umlinteraction_name".
- 存在多个名为
umlinteraction_name
的顺序图模型时,输出上述内容。
- 存在多个名为
Failed, lifeline "lifeline_name" in umlinteraction "umlinteraction_name" not found.
- 顺序图模型
umlinteraction_name
中不存在名为lifeline_name
的参与对象时,输出上述内容;
- 顺序图模型
Failed, duplicated lifeline "lifeline_name" in umlinteraction "umlinteraction_name".
- 顺序图模型
umlinteraction_name
中存在多个名为lifeline_name
的参与对象时,输出上述内容;
- 顺序图模型
说明:
- Found 消息为来自 Endpoint 的消息,Lost 消息为发送至 Endpoint 的消息。
五、模型有效性检查
该部分为新增指令。
模型有效性检查部分,将在实例化完毕后自动按序触发执行,而不需要通过指令的形式执行。执行中一旦发现不符合规则的情况,将直接退出,不进行后续有效性检查和指令查询。
R001: 类图元素名字不能为空(UML 001)
规则解释:
-
目前所有类图的元素中,除了以下元素之外,其余元素的
name
字段均不能为空:direction
为return
的 UMLParameter- UMLAssociation
- UMLAssociationEnd
- UMLGeneralization
- UMLInterfaceRealization
输出:
Failed when check R001, a certain element doesn't have name.
- 如未发现此问题,则不需要任何输出,否则输出上述内容。
R002:针对下面给定的模型元素容器,不能含有重名的成员(UML 002)
规则解释:
- 针对类图中的类(UMLClass),其成员属性(UMLAttribute)和关联的另一端所连接的 UMLAssociationEnd 这两者构成的整体中,不能有重名的成员。
输出:
Failed when check R002, "member" in "Container", "member2" in "AnotherContainer" has duplicated name.
- 如未发现此问题,则不需要任何输出,否则输出上述内容;
- 需要输出所有重名的成员名,以及它们所在的类。此例中,一共有两个存在重名的成员,分别是
Container
类中的member
,与AnotherContainer
类中的member2
。
说明:
- 如果一个类中有多个同名的成员,则这些成员名只需要输出一次;
- 如果模型中有多个模型元素违背 R002,则依次输出,次序不敏感,接口会在输出前进行排序;
- 此规则无需考虑继承关系,也不需要考虑接口实现关系;
- 一个类下的属性和关联的另一端所连接的 UMLAssociationEnd 也不可出现重名。注意理解关联的另一端的意思;
- 特殊规定:若有两个 UMLAssociationEnd 的
name
字段都为空,则规定这两个成员不是重名。
R003:不能有循环继承(UML008)
规则解释:
- 该规则只考虑类的继承关系以及接口之间的继承关系。所谓循环继承,就是按照继承关系形成了环。
-
例如下面的场景:
1 2 3 4 5 6 7
interface A extends B { // something here } interface B extends A { // something here }
这里就构成了一组最简单的循环继承。
输出:
-
Failed when check R003, class/interface (A, B, C, D) have circular inheritance.
- 如未发现此问题,则不需要任何输出,否则输出上述内容;
- 需要列出所有在循环继承链中的类或接口名。此例中,一共有 4 个存在循环继承的类或接口,分别是 A、B、C、D。
说明:
- 对于同一个类和接口,只需要输出一次即可;
- 如果模型中有多个模型元素违背 R003,则依次输出,次序不敏感,接口会在输出前进行排序;
- 输出的集合中需要包含全部继承环上的类和接口。
R004:任何一个类或接口不能重复继承另外一个类或接口(UML007)
规则解释:
- 该规则考虑类之间的继承关系、接口之间的继承关系,包括直接继承或间接继承。
-
例如下面的场景
1 2 3 4 5 6 7 8 9 10 11
interface A { // something here } interface B extends A { // something here } interface C extends A, B { // something here }
接口 C 就重复继承了接口 A(一次直接继承,一次通过接口 B 间接继承)。
输出:
-
Failed when check R004, class/interface (A, B, C, D) have duplicated generalizations.
- 如未发现此问题,则不需要任何输出,否则输出上述内容;
- 需要列出所有带有重复继承的类或者接口名。此例中,一共有 4 个存在重复继承的类或接口,分别是 A、B、C、D。
说明:
- 如果存在多个直接或间接重复继承了其他的类或接口的类或接口,则按照任意顺序传出即可,次序不敏感,接口会在输出前进行排序。
- 值得注意的是,本次作业的本条限制,同样也禁止了接口的重复继承。然而接口重复继承在 Java 8 中实际上是允许的,也就是说,这是 UML 本身的一条合法性规则,无关语言。请各位判断的时候务必注意这件事。
R005: 接口的所有属性均需要为 public(UML 011)
规则解释:
- 接口属性的可见性需要为
public
。
输出:
Failed when check R005, all attributes and operations of interface must be public.
- 如未发现此问题,则不需要任何输出,否则输出上述内容。
说明:
- 在我们的模型中,接口不会有方法,因此只需要检测属性。
R006:Lifeline 必须表示在同一 Sequence Diagram 中定义的 Attribute
规则解释:
- Lifeline 的
represent
属性必须指向在同一 Sequence Diagram 下定义的Attribute
。
输出:
Failed when check R006, each lifeline must represent an attribute.
- 如未发现此问题,则不需要任何输出,否则输出上述内容。
R007:一个 Lifeline 在被销毁后不能再收到任何消息
规则解释:
- 一个
Lifeline
是一个实例对象,在被销毁(即收到 Delete 消息)后再收到任何消息都是不合理的。
输出:
Failed when check R007, a lifeline receives messages after receiving a delete message.
- 如未发现此问题,则不需要任何输出,否则输出上述内容。
说明:
- 在官方包导出的模型中,消息的顺序与输入的顺序相同。越早输入的 UMLMessage 元素,代表了越早的消息。
R008:所有 Final state 状态不能有任何迁出(UML 033)
规则解释:
- 所有 Final state 不能有任何迁出。
输出:
Failed when check R008, a final vertices has at least one outgoing transition.
- 如未发现此问题,则不需要任何输出,否则输出上述内容。
R009:一个状态不能有两个条件相同的迁出
规则解释:
- 如果存在一个状态的所有的迁出 Transition 的所有触发事件(Trigger)中有两个相同的 Trigger,那么这两个 Trigger 所在的 Transition 必须拥有不为空且逻辑上不能同时成立的守护条件(Guard);
- 如果不满足这条规则,就会存在某种可能性,使得从这个状态开始不知道走哪一个转移。
输出:
Failed when check R009, a state has ambiguous outgoing transition.
- 如未发现此问题,则不需要任何输出,否则输出上述内容。
说明:
- 为了简化问题,将“逻辑上不能同时成立的 Guard”解释为“字符串比较不相同”;
- 为了简化问题,将“两个 Trigger 相同”解释为“它们的
name
字段字符串相同”。
数据限制
- 全局限制
- 所有公测数据不会对接口中定义的方法、类属性(static attribute)、类方法(static method)做任何测试要求。
- 类图相关
- 对于多继承问题,我们保证在类图数据中,类一定没有多继承,但是接口可能有多继承(即与 Java 8 规范相同)。
- 顺序图相关
- 无相关保证。
- 状态图相关
- 保证每个 State Machine 中有且仅有一个 Region;
- 保证每个 State Machine 中,有且仅有一个 Initial State,有零个或多个 Final State;
- 保证每个 State Machine 中 Initial State 和 Final State 的 name 均为 null,查询指令中给出的状态也不会为 Initial State 或 Final State;
- 保证每个 State Machine 中,Initial State 没有状态迁入;
- 保证每个 State Machine 中不包含复合状态。
题目分析
刚刚开始我是有点懵的,特别是看到所谓的官方接口,还有导入文件的一堆东西,确实有点无从下手的感觉。
其实官方给的包,就是帮我们将UML当作文本文件一样,对其内容进行处理和加工,使得其变成约定的输出格式。它输出给我们的一样就是所谓的一个UMLElement,我们从中提取出需要的信息即可。而要求指令部分,大部分是没什么难度的,有一些需要找父亲之类的就用BFS判别就好了。和第三单元很像,重点在于理解指令和测试对拍上。此外这次要对自己的程序有比较好的封装,要不然很容易超过500行的限制。我是将大多数UmlElement在建了一个类,对其知识的操作进行封装,也对与其有关的别的U目录Element进行关联
diagram包里面是有关图的类型的建模,因为做到第三次的时候行数超了,就再对模型进行了提炼。建立新的抽象类。在每个具体的diagram里面进行对元素的解析,在Implementation中调用相对应的解析方法或者计算方法即可,也是对程序的进一步封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyImplementation implements UserApi {
//储存每一个UMLElement
private final HashMap<String, Object> id2elements = new HashMap<>();
//类图
private final ClassDiagram classDiagram = new ClassDiagram(id2elements);
//序列图
private final StateDiagram stateDiagram = new StateDiagram(id2elements);
//状态图
private final SequenceDiagram sequenceDiagram = new SequenceDiagram(id2elements);
//需要解析时,调用对应图的方法
private void parseClass(UmlClass umlClass) {
classDiagram.parseClass(umlClass);
}
//题目要求的方法也封装在对应的Diagram里面
public UmlLifeline getParticipantCreator(String interactionName, String lifelineName)
throws InteractionNotFoundException, InteractionDuplicatedException,
LifelineNotFoundException, LifelineDuplicatedException,
LifelineNeverCreatedException, LifelineCreatedRepeatedlyException {
return sequenceDiagram.getParticipantCreator(interactionName, lifelineName);
}
//判断是否合乎RUle,也是如此
public void checkForUml003() throws UmlRule003Exception {
classDiagram.checkForUml003();
}
}
对于所有的UMLElement,用一个map进行储存,其中key为ID,value为具体的Element或者是衍生类(如MyClass)这种的,这样在寻找Parent或者与其他类相关时,会很方便,因为UML中对于其他类的连接都是建立在ID的基础之上的。其中所有的Diagram级别的元素共用一个Map,这样每个元素在内部都可以访问所有的UMLModel
1
2
3
4
//类图的初始化
public ClassDiagram(Map<String, Object> id2Elements) {
this.id2elements = id2Elements;
}
此外还有一点需要注意,UMLElement出现的顺序是随机的,因此可能先出现UMLAttribute再出现所对应的UMLClass这样的情况,因此我们在建模的时候,要多循环几次,按照层次,每次把同一层次的元素提取出来,一层一层建模
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public MyImplementation(UmlElement... elements) {
for (UmlElement element : elements) {
if (element instanceof UmlClass) {
count++;
parseClass((UmlClass) element);
} else if (element instanceof UmlInterface) {
parseInterface((UmlInterface) element);
}
}
for (UmlElement element : elements) {
if (element instanceof UmlAttribute) {
parseAttribute((UmlAttribute) element);
} else if (element instanceof UmlOperation) {
parseOperation((UmlOperation) element);
} else if (element instanceof UmlAssociationEnd) {
parseAssociationEnd((UmlAssociationEnd) element);
} else if (element instanceof UmlGeneralization) {
parseGeneralization((UmlGeneralization) element);
} else if (element instanceof UmlInterfaceRealization) {
parseInterfaceRealization((UmlInterfaceRealization) element);
} else if (element instanceof UmlStateMachine) {
parseStateMachine((UmlStateMachine) element);
} else if (element instanceof UmlInteraction) {
parseInteraction((UmlInteraction) element);
}
}
for (UmlElement element : elements) {
if (element instanceof UmlAssociation) {
parseAssociation((UmlAssociation) element);
}
if (element instanceof UmlParameter) {
parseParameter((UmlParameter) element);
} else if (element instanceof UmlRegion) {
parseRegion((UmlRegion) element);
} else if (element instanceof UmlLifeline || element instanceof UmlEndpoint) {
parseLifeline(element);
}
}
for (UmlElement element : elements) {
if ((element instanceof UmlState) || (element instanceof UmlFinalState)
|| (element instanceof UmlPseudostate)) {
parseState(element);
} else if (element instanceof UmlMessage) {
parseMessage((UmlMessage) element);
}
}
for (UmlElement e : elements) {
if (e instanceof UmlTransition) {
parseTransition((UmlTransition) e);
}
}
for (UmlElement element : elements) {
if (element instanceof UmlEvent) {
parseEvent((UmlEvent) element);
}
}
}
通过这样就可以依次按照层次将每一个元素存储起来,并建立其中的联系