oo期末总结
作者:互联网
OO 总结
目录
总结本单元作业的架构设计
总结自己在四个单元中架构设计思维及OO方法理解的演进
总结自己在四个单元中测试理解与实践的演进
总结自己的课程收获
立足于自己的体会给课程提三个具体的改进建议
本单元总结
作业要求与实现细节
hw13
查询指令(仅限类图)
- 类数量
- 类的子类数量
- 类的操作数量
- 类的操作可见性
- 类的操作的耦合度
- 类的属性的耦合度
- 类实现的全部接口
- 类的继承深度
作业整体分为了两个部分
- 对类图结构的划分与
MyImplement
的初始化 - 查询指令的完成
层次划分
首先,由于类图中各个元素存在依赖关系,且输入时乱序输入,所以有必要将各个元素划分为不同层级
- class、interface
- attribute、operation、interfaceRealization、associationEnd
- parameter、generalization、association
完整的顺序是:
- class、interface、collaboration、statemachine
- attribute、operation、interfaceRealization、associationEnd、region、interaction
- parameter、generalization、association、lifeline、preudostate、state、finalstate
- message、transition
- event
每一层级封装为一个while循环的函数,依次处理
Tool工具类——数据结构
之后,完成各个查询指令,为了解决元素间重名的问题,新建了一个工具类Tool
并定义了若干查询功能
public class Tool<T>{
private HashMap<String, T> id2element = new HashMap<>();
private HashMap<String, ArrayList<T>> name2element = new HashMap<>();
}
这一数据结构被广泛应用于元素的存储,同时可以借助这一数据结构完成相应的查询以及异常的判断
instruction7——查询所实现接口
类实现接口有三种方式:
- 直接实现接口
- 实现了接口的父类
- 继承了父类实现的接口
为此,该指令的实现思路为:
- 找到查询类的所有“祖辈”
- 获取各祖辈所实现的接口集合(从已有接口获取其祖辈接口)
HashSet<MyInterface> interfaceSet = new HashSet<>(interfaces.getValue());
HashSet<MyClass> fatherCollection = new HashSet<>();
MyClass tmpFather = father;
while (tmpFather != null) {
fatherCollection.add(tmpFather);
tmpFather = tmpFather.getFather();
}
//从father集合获取各father直接实现的接口
for (MyClass f : fatherCollection) {
interfaceSet.addAll(f.getInterfaces().getValue());
//interfaceSet.addAll(f.getInterfaces().getNameKeys());
}
//接下来要找interface的父亲块
HashSet<MyInterface> interfaceFatherBlock = new HashSet<>();
for (MyInterface i : interfaceSet) {
HashSet<MyInterface> ifatherBlock = i.getAllFather();
interfaceFatherBlock.addAll(ifatherBlock);
}
//从已有接口获取其父类接口
interfaceSet.addAll(interfaceFatherBlock);
ArrayList<String> interfaceNames = new ArrayList<>();
for (MyInterface realization : interfaceSet) {
interfaceNames.add(realization.getName());
}
implementsName = interfaceNames;
return interfaceNames;
hw14
查询指令(新增顺序图和状态图)
状态图
- 指定状态机(
statemachine
)查询状态数量 - 判断指定状态是否为状态机中的关键状态
- 两个给定状态间引发状态迁移的所有触发事件
顺序图
- 顺序图参与对象数量(包含lifeline与endpoint)
- 查找对象的创建者
- lost和found消息数量查询
指令分析
实现较为复杂的指令有
- 关键状态判断
- 状态间所有触发事件
- 有关消息的查询
关键状态判断
首先,对于关键状态的判断,其最后发生在region中(即state的parent),判断逻辑为:
- 若由初始状态(preudostate,唯一)无法到达任意的结束状态(finalstate),则当前查询的状态为非关键状态
- 若可以,考虑删除掉当前查询状态,判断从初始状态是否可到达某一结束状态,若可,则当前查询状态非关键状态;若非,则是关键状态
判断是否可到达:(由于本次作业的时间要求较宽松,所以采用了最朴素的方法)
public boolean reachable(MyState s1, MyState s2, MyState except) {
HashSet<MyState> level = new HashSet<>(s1.getLinked());
HashSet<MyState> visited = new HashSet<>(s1.getLinked());
visited.add(s1);
level.remove(except);
while (!level.isEmpty()) {
if (level.contains(s2)) {
return true;
}
HashSet<MyState> tmp = new HashSet<>();
for (MyState levelX : level) {
HashSet<MyState> tmpX = new HashSet<>(levelX.getLinked());
for (MyState next : tmpX) {
if (!visited.contains(next) && next != except) {
tmp.add(next);
}
}
}
level = tmp;
visited.addAll(level);
}
return false;
}
message查询
关于lifeline/endpoint收到或发出的message,建立了如下数据结构
private HashMap<MessageSort, Tool<MyMessage>> asSource = new HashMap<>();
private HashMap<MessageSort, Tool<MyMessage>> asTarget = new HashMap<>();
使用了二重索引,便于根据message的种类索引相应的message。
同时,在初始化时,将lifeline和endpoint归为一类,但为endpoint特别加上isEnd
的属性判别,在interaction中也专门设置一个容器存放endpoint,这样可以在查找lost和found消息时更快速,统一管理可以接受消息的对象,同时不至于混淆lifeline与endpoint。
hw15
模型有效性检查
hw15集中在模型有效性检查,其顺序执行,发生在查询指令之前,若检测到违反规则,立刻抛出异常并退出
- R001:查询类图部分元素的name字段是否为空
- R002:重名成员判断
- R003:循环继承
- R004:重复继承
- R005:接口属性的可见性
- R006:Lifeline所represent的属性是否与其在同一个collaboration
- R007:delete后的lifeline不能收到消息
- finalstate不存在迁出
- 同一状态不能有两个条件相同的迁出
字段为空的判断
(weak1就寄在这里)
最初采用正则表达式,但是无奈学艺不精出现了bug,最终采用朴素的方式解决
- 特判null为空
- 替换字段中所有空格和制表符为空
- 判断新的字段是否长度为0
R002-重名成员判断
(这一段指导书写的很绕,所以理解这一条有效性判断花了一部分时间)
关于关联的另一端,结合mdj文件理解为一个类所管理的元素有
- 若干attribute
- 若干associationEnd(R002只考虑关联到的end,不考虑自身所对应的end,虽然mdj里面把自身对应的end也算上了)
- 若干operation(R002并不考虑)
R002要检查的就是在一个类里面若干个associationend attribute 互相之间不能有重名,如果有重名输出成员名字和当前类的名字。
另外,如果有两个end的name都为空不记为重名,如果attribute和end重名且为空或者多个为空的attri记为异常
//在群里看到有位同学的图,感觉这个理解非常恰当!
public class foo{
private sometype attri1;
private sometype attri2;
private class2 end1;
}
R003/R004 类的继承
类的循环继承:
- class:由开始类出发,向上搜寻,直到无法前进,若可以再经过开始的类,证明循环继承。这个方式要注意如果开始类的祖辈循环继承,则不会到“无法前进”的时刻,选择在折返的时候命
current=null
结束循环 - interface:类似地,遍历得到A所有能继承的接口,如果里面出现了A就说明循环继承,遍历结束条件是新的一层祖辈接口为空
类的重复继承:
- class:私认为类是不存在重复继承的异常的,因为题目限制了一个类发出的generalization行为只能有一个,无论是指向同一个类还是其他类
- interface:方法依然是找到该接口所能继承的所有类,如果一个接口两次出现,那么引发异常
(上面的方法在实现的时候有一个小问题,就是visited里面没有加入最开始的那个接口,就是说,如果接口循环继承回来了不算为重复继承,老实说我也不知道应该算还是不算,但是这个情况过不了R003所以不会进入R004的判断所以无需考虑)
测试环节
对于前两次作业主要采用了自动化测试的方法,根据输入的uml图语法及json规则随机生成uml图与查询指令。
第三次作业需要针对性地生成无效模型,所以放弃了自动化测试的想法转为手搓数据,构造思路为覆盖有效及无效的每一种情况。例如对R001的测试
- name均不为空
- class为空
- interface为空
- attribute为空(in class/interface/collaboration(不抛出异常))
- operation为空
- parameter为空(in/return(不抛出异常))
- association associationEnd realization generalization 及其他图中元素为空(不抛出异常)
架构设计思维及OO方法理解的演进
本学期的oo课程带给我很大的收获,虽然一开始每周一次的ootime带给我很大的负担,强测和互测让人心理压力很大,但是经过一学期的锻炼,现在已经可以坦然在最后一天开始动手写了(bushi)。整体来说oo让我对java语言有了一个初步的熟悉,同时在实验里面逐步了解面向对象的思想。
本学期的oo课程一共分了四个单元,分别是:
- 多项式计算
- 多线程初识,电梯系统模拟
- JML规格要求下的代码书写
- UML图解析
个人对四个单元的难度排序是2>1>4>3
(电梯单元太痛苦了)
第一个单元算是oo的初步试水,一开始非常手忙脚乱,最终目的是过评测,对于面向对象的理解不是很深,评测过于依赖评测机,在构造数据自测方面没有做出努力,手搓的数据不能覆盖全面的情况。导致第一单元出现了一些非常简单但没发现的bug
第二单元重点是对多线程的掌握。三四单元则轻松起来了,也是从三四单元开始我终于下定决心要自己搭评测机和生成随机数据。三四单元的另一个收获是认真阅读指导书,对事件发生情况进行全面的考虑,以达到全面的覆盖测试。
课程收获
这学期的oo课程让我学到了很多,除了前面提的编程能力的提高,还有一些设计思维上的考虑。
荣文戈老师一直在强调不要上来就开始动笔写,要先设计好。一学期的实验充分验证了这句话,在一开始就考虑扩展空间会省去后面重构的时间和精力。
另外还有抗压能力的提升,最开始看到强测出大锅会产生很大的心理压力,现在变得更平和了,不就是bug嘛,非常正常,修就完了!
课程建议
- 第一单元的难度可以稍微降低一下,或是pre的难度稍微抬高一点,给出更充足的缓冲过渡阶段
- 提高中测的难度,稍微降低下中测和强测的梯度差异
- 希望能对一些课程实验具体内容之外的东西进行扩充,比如评测机搭建的方法
标签:oo,总结,HashSet,接口,查询,期末,为空,new,单元 来源: https://www.cnblogs.com/zjh-buaa/p/16422657.html