面向对象第四单元及学期总结
面向对象第四单元及学期总结
本单元架构设计
本单元基于对UML
图的理解,实现对输入UML
图的查询操作。
第一次作业
第一次作业只涉及类图的元素,每一个UmlElement
有独一无二的id
,且有唯一的一个父亲parent_id
,即建立树状的层次结构,如一个UMLClass
包含多个UMLAttribute
和UMLOperation
,一个UMLOperation
又可以包含多个UMLParameter
。对于这些需要存储与下一级元素关系的类进行了进一步封装,建立了自己的MyUmlInterface
、MyUmlOperation
、MyUmlClass
三个类。
由于UmlElement
按照id输入按照名字查找,故在顶层工具类MyImpletation
保存了所有id
与其UmlElement
和name
与其id
的HashMap
映射,经过多次循环遍历存储元素信息并建立关系。
init1 |
UML_CLASS,UML_INTERFACE,UML_ASSOCIATION_END |
---|---|
init2 |
UML_ASSOCIATION,UML_ATTRIBUTE |
init3 |
UML_OPERATION,UML_GENERALIZATION,UML_INTERFACE_REALIZATION |
init4 |
UML_PARAMETER |
第二次作业
增加了对顺序图和状态图的查询,具体操作基本同第一次作业,一些方法的查询需要本人的难点在于一些图算法的实现......比较困难的指令例如:给定状态机模型和其中的一个状态,判断其是否是关键状态STATE_IS_CRITICAL_POINT statemachine_name statename
我的做法是在初始化完所有UmlElement
后遍历所有stateMachine
查找关键状态并存储,调用指令时直接返回。
需要注意的就是搜索时对于空指针等特殊情况的判断...
public void findCriticalState() {
if (finalStates.size() == 0) {
return;
}
List<String> chain = new ArrayList();
for (int i = 0; i < finalStates.size(); i++) {
final2critical.add(new ArrayList<>());
dfs(pseudostate.getId(), finalStates.get(i).getId(), chain, paths);
if (paths.size() == 0) { continue; }
List<String> list = paths.get(0);
int j = 0;
for (int k = 0; k < list.size(); k++) {
String id = list.get(k);
for (j = 1; j < paths.size(); j++) {
if (!paths.get(j).contains(id)) {
break;
}
}
if (j == paths.size() && !finalStatesId.contains(id)) {
final2critical.get(i).add(id);
}
}
chain.clear();
paths.clear();
}
int k;
for (int i = 0; i < finalStates.size(); i++) {
List<String> list = final2critical.get(0);
for (int j = 0; j < list.size(); j++) {
String id = list.get(j);
if (finalStates.size() > 1) {
for (k = 1; k < finalStates.size(); k++) {
if (!final2critical.get(k).contains(id)
&& final2critical.get(k).size() != 0) {
break;
}
}
if (k == finalStates.size()) {
nameOfCriticalState.add(id2state.get(id).getName());
}
} else {
nameOfCriticalState.add(id2state.get(id).getName());
}
}
}
}
public void dfs(String curId, String finalId, List<String> chain, List<List<String>> res) {
MyState curState = id2state.get(curId);
if (curState.getName() == null && !curId.equals(pseudostate.getId())) {
if (curId.equals(finalId)) {
res.add(new ArrayList<>(chain));
}
return;
}
for (String linkedId : curState.getLinkedStateId()) {
if (chain.contains(linkedId)) { continue; }
chain.add(linkedId);
if (!linkedId.equals(curId)) {
dfs(linkedId, finalId, chain, res);
}
chain.remove(chain.size() - 1);
}
}
第三次作业
检查UML
模型中非法的地方并指出其类型。具体实现可分为在Initialize
循环初始化时进行检查、在初始化完成后基于已经构建好的树结构再进行遍历检查两种方式。对于大部分不需要存储具体出错的UmlElement
的check
,选择设置一个全局变量flag
,在初始化过程中就判断修改标志flag
。
比较困难的实现是对于循环继承和重复继承的检查。
循环继承中类与接口分开处理,鉴于类的单继承特性只需要不断查找其父类并在这个过程中存储路径;对于可以多继承的接口,采用tarjan
算法找出所有size>=2
的连通块,另外在初始化时单独判断自环的情况。
重复继承只需要考虑接口。对于每一个接口按照树形结构一层一层更新自己的父接口集时,应该是对原有的扩充,不应出现重复,如果出现重复就把子接口加入集合。
同时为了减少工具类的代码量尽可能让各个类各司其职,单独提取了MyCheck
、MyInit
类分别用于遍历检查和完成部分初始化工作。
四个单元中架构设计思维及OO方法理解的演进
第一单元
没做出来、可能需要去补给站回炉重造一下。
第二单元
吸取了一些上一单元大失败的教训加上课程组心慈手软给出了实验代码将架构层次给的很清晰,基本按照这个结构就能完成对于一个请求的流水线式分配与处理。接触了一点设计模式,使用到的有生产者消费者模式和观察者监听者模式。容易出问题的是多线程的轮询、死锁,synchronized
和notify
的使用问题基本贯穿始终。同时最后一次作业的多线程的异步性导致的大bug
也反映了合理设计对象间的交互的重要性。
第三单元
本单元整体的架构基本基于所选存储容器,类之间的关系结构通过规格可以得出。JML
规格是基准,具体实现可以自行变通,比如使用缓存保留一些曾经计算过的值等方法节约时间。一些卡TLE
的点也印证了在正确的基础上实现高效的重要性。
第四单元
理清要实现的架构是编写代码的先验条件。每个元模型的含义、内部属性、与其他元模型的关系是我们要深入研究的对象。除了对UML
模型更加了解外,再一次体会到了不同类依次向下调用方法的清晰思路,这减少了很多顶层的代码量,并且依次调用的逻辑清晰,在debug
时也能很快定位到究竟是哪一个方法出了问题。
四个单元中测试理解与实践的演进
- 第三单元比较便于使用评测机对拍尝试了一下,还是能找到一点
bug
的,不过由于数据覆盖率不高... - 手动构造:在对指导书理解的基础上构造。看基本的方法实现的正确性,同时使用边缘数据考察性能和极限情况。
课程收获
- 会写一点点Java、能实现简单的一点点多线程任务、会阅读JML规格;了解了面向对象思想、多线程思想、Java设计模式;通过三四单元证明了数据结构学了个寂寞,需要回炉重造一下,了解了算法实现也很重要;体会到了代码框架设计和测试这二者的重要性。
改进建议
- 是否可以考虑增加一些关于测试的学习内容,当然这可能只是本蒻自己的问题...U•ェ•*U
- 实验代码公开评测结果或者结束后下发答案?记得多线程单元第二次实验的流水线代码挺复杂的,没写出来,事后也没接着研究了/(ㄒoㄒ)/~~
- 考虑在pre中渗透第一单元思想比如递归下降?只会正则表达式并不能解决问题...(血泪ε(┬┬﹏┬┬)3