2019面向对象程序设计第四单元总结与课程感想
写在前面
OO第四单元是对解析UML模型化设计的考验,对类图、顺序图、状态图进行解析,并进行一致性检查。此外,读、画UML图也能极大程度上锻炼对模型化设计的理解。UML是与用户打交道的常用工具,直观的UML图能够方便对方对于自己设计架构的了解,因此学习UML具有非常重要的意义。
的意义。
一.第四单元设计架构
本单元主要有两项任务:
-
对UML类图进行解析。
-
对顺序图和状态图进行解析,并且能够对规则进行验证。
Ⅰ.第一次作业
思路:自顶向下层次化构建类图
1.第一层:从众多元素中解析类、接口和UML_ASSOCIATION_END
private void handleELe1(UmlElement element) {
String type = element.getElementType().toString();
if (type.equals("UML_CLASS")) {
MyClass newClass = new MyClass(element);
if (this.name2Classes.containsKey(element.getName())) {
this.name2Classes.get(element.getName()).add(newClass);
this.id2class.put(element.getId(), newClass);
} else {
LinkedList<MyClass> myClasses = new LinkedList<>();
this.name2Classes.put(element.getName(), myClasses);
this.name2Classes.get(element.getName()).add(newClass);
this.id2class.put(element.getId(), newClass);
}
} else if (type.equals("UML_INTERFACE")) {
MyInterface myInterface = new MyInterface(element);
this.id2Interface.put(element.getId(), myInterface);
} else if (type.equals("UML_ASSOCIATION_END")) {
UmlAssociationEnd end = (UmlAssociationEnd) element;
this.id2end.put(element.getId(), end);
}
}
2.第二层:向已经构建好的类中添加方法、类变量、继承关系、接口实现等
private void handleEle2(UmlElement element) {
String type = element.getElementType().toString();
if (type.equals("UML_OPERATION")) {
if (this.id2class.containsKey(element.getParentId())) {
this.id2class.get(element.getParentId()).addOperation(
(UmlOperation) element);
MyOperation myOperation = new MyOperation(element);
this.id2Operation.put(element.getId(), myOperation);
}
} else if (type.equals("UML_ATTRIBUTE")) {
if (this.id2class.containsKey(element.getParentId())) {
this.id2class.get(element.getParentId()).addAttribute(
(UmlAttribute) element);
}
} else if (type.equals("UML_GENERALIZATION")) {
if (this.id2class.containsKey(element.getParentId())) {
this.id2class.get(element.getParentId())
.addGeneralization((UmlGeneralization) element);
} else if (id2Interface.containsKey(element.getParentId())) {
id2Interface.get(element.getParentId())
.addGeneralization((UmlGeneralization) element);
}
} else if (type.equals("UML_INTERFACE_REALIZATION")) {
if (this.id2class.containsKey(element.getParentId())) {
this.id2class.get(element.getParentId())
.addInterface((UmlInterfaceRealization) element);
}
} else if (type.equals("UML_ASSOCIATION")) {
UmlAssociation association = (UmlAssociation) element;
//first get Association End id
String endId1 = association.getEnd1();
String endId2 = association.getEnd2();
//use id to get Association End
if (this.id2end.containsKey(endId1)) {
UmlAssociationEnd end1 = this.id2end.get(endId1);
//if class1 exists:add class id 2 to associations
if (this.id2class.containsKey(end1.getReference())) {
this.id2class.get(
end1.getReference()).addAssociation(
this.id2end.get(endId2).getReference());
}
}
if (this.id2end.containsKey(endId2)) {
UmlAssociationEnd end2 = this.id2end.get(endId2);
// if class2 exists: add class id1 to associations
if (this.id2class.containsKey(end2.getReference())) {
this.id2class.get(
end2.getReference()).addAssociation(
this.id2end.get(endId1).getReference());
}
}
}
}
3.第三层:向类方法中添加方法变量
private void handleELe3(UmlElement element) {
String type = element.getElementType().toString();
//now operations already init, add parameters
if (type.equals("UML_PARAMETER")) {
String id = element.getParentId();
if (this.id2Operation.containsKey(id)) {
this.id2Operation.get(id).addParameter((UmlParameter) element);
}
}
类图:
度量分析:
注:
ev(G)为Essentail Complexity,表示一个方法的结构化程度
iv(G)为Design Complexity,表示一个方法和他所调用的其他方法的紧密程度
v(G)为循环复杂度
OCavg为平均循环复杂度
WMC为总循环复杂度
Ⅱ.第二次作业
思路:自顶向下构建顺序图以及状态图、独立化模型有效性检查。
整体构架如下:
循环继承检查的递归实现
//this tool function is used for searching cycle
public static void cycleJudge(
String id, HashMap<String, HashMap<String, Integer>> name2generals,
LinkedList<String> relatedIds, HashSet<String> faultId008) {
//first get the index inside the illegalIdList
int index = relatedIds.indexOf(id);
//System.out.println(index);
//if index is -1, means class not found
if (index != -1) {
for (int i = index; i < relatedIds.size(); i++) {
//System.out.println("we are here!");
faultId008.add(relatedIds.get(i));
}
} else {
/*else the class is already illegal,
and the class after this class are all wrong
*/
relatedIds.addLast(id);
//System.out.println(relatedIds.size());
Iterator<String> it = name2generals.keySet().iterator();
while (it.hasNext()) {
String nextId = it.next();
//use it's extended class to find whether it's illegal
if (name2generals.get(id).containsKey(nextId)) {
cycleJudge(nextId, name2generals, relatedIds, faultId008);
}
}
relatedIds.remove(relatedIds.size() - 1);
//System.out.println(relatedIds.size());
}
}
类图:
度量分析:
注:
ev(G)为Essentail Complexity,表示一个方法的结构化程度
iv(G)为Design Complexity,表示一个方法和他所调用的其他方法的紧密程度
v(G)为循环复杂度
OCavg为平均循环复杂度
WMC为总循环复杂度
二.在四个单元中的架构设计以及对OO方法的理解
1.第一单元:
第一单元的核心主题是表达式求导:
- 第一次作业,简单的加法求导,熟悉并掌握了基本Java语法,比较基础,强测拿到100分,互测拿到10分。
- 第二次作业,加入了三角函数sinx与cosx的求导。所以要开始分拆成几个类去分别计算了。强测拿到99+分,位列第11,互测首次被hack,让我认识到核心科技评测机的重要性。
- 第三次作业,是结合架构,对递归下降分析的深入理解与应用。这次作业,我开始真正意义上考虑架构问题,首次完成了自主构建评测机。强测拿到96+分,位列前10,互测拿到20+分。
2.第二单元:
第二单元的核心主题是多线程:
- 第一次作业,先来先服务的傻瓜电梯调度模式很基础。
- 第二次作业,开始多线程的尝试,这次作业是我OO学习历程中的一大失误,没有进行自主测试,线程安全没有处理好,强测惨烈,互测难以回天。给我敲响警钟。
- 第三次作业,多部电梯调度。真正意义考验对于面向对象编程思想和优化架构的理解。进行了自主测试。
3.第三单元:
第二单元的核心主题是JML规格化设计:
- 第一次作业,对路径的简单管理,较为轻松。
- 第二次作业,对图的管理。继承上次作业,构建图数据结构,利用BFS、Dijkstra或者Floyd等算法进行最短路径查询。
- 第三次作业,对数据结构与算法要求较高,有较大的技巧性,对最低票价、最小不满意度的查询可以考虑拆点做法。
4.第四单元:
第二单元的核心主题是UML模型化设计:
- 第一次作业,自顶向下逐步还原类图,比如:class->operation->parameter层次化设计。
- 第二次作业,实现自顶向下实现顺序图、状态图的解析器,并且进行规格检查让我加深了对于类、接口、继承、接口等概念的理解。
三.四个单元中测试理解与实践的演进
在OO刚开始时,我对自动化测试还没有很明确的概念,在前两次作业中,我依靠手动构造测试样例进行测试,同学们群策群力,互相出测试数据。但在第二次作业中,我在互测中被别人hack却没有找到别人的bug,这给了我很大压力,也让我认识到自动化测试的重要性。从多项式第三次project开始,每次有较大难度的作业我都构造评测机或写互拍程序,在互测中斩获颇丰。
从OO第三单元开始,学习了Junit的使用之后,我采取随机数据进行覆盖测试和Junit单元测试相结合的方式进行bug检查,在强测中未出现过bug。
第一单元评测:利用python脚本+matlab对多项式进行取值检验,并进行互拍
第二单元评测:修改官方接口,进行时间检验。同时进行电梯逻辑检验
第三单元评测:除了正确性检查之外,支持CTLE的检查
四.我的课程收获
从面向过程到面向对象的转变,从1个java类到多个类的层次化设计,从百行代码到千行代码的真正飞跃。
架构思考与设计。开始学习OO之后,尤其是从电梯单元开始,开始足够重视架构的设计,这使我能够进行安全的设计。
进行自动化测试和模块化测试。利用大数定律进行随机数据点覆盖测试以及利用Junit进行模块化测试能够有效地检查bug,前三个单元我均进行了自主测试。并且从第二单元第二次电梯作业失手中,我深刻体会到了测试的重要性。
在代码风格方面,为了达到课程组对于代码风格的要求,我开始真真正正地对自己的函数和类进行规划和设计,避免冗余和达到行数限制。
架构设计是OO课程的精髓之一,如果有好的架构,就无需重构,类的耦合度低。在每次作业设计架构的过程中,我深刻理解了了数据封装的思想,和访问控制以及继承、接口实现、异常处理等方面的知识。
在测试的过程中,我和其他几位同学每次作业都分工完成评测机,并且收获了许多跨系友谊。在构建评测机与互测的过程中,极大程度地锻炼了我的python、shell 代码能力,学习OO一门课,我的代码能力有了显著提高。
可以肯定的是,今年的OO课程相比之前有了巨大的改进,老师和助教们大刀阔斧的改革的勇气和魄力让人敬佩。面向对象设计与构造这门课是我进入计算机专业之后体验最好的一门课,接近万行的源码体现助教们的无私奉献,每次作业的官方论坛都让人获益匪浅,指导书条理规范,基本没有歧义,每次作业的标程让我明白什么是好的架构,每两周一次的研讨课能够集思广益,看到优秀同学们的思路。
一周周的作业让我感到紧张,但当通过中测以及看到很棒的强测分的时候,便感到自己的努力终究没有白费。多年之后,回想起OO之旅,肯定也会心潮澎湃,因为在这四个月的旅程中,没有遗憾,全是满满的收获。在最后,感谢诸彤宇老师的悉心教授,和张少昂、张哲维助教的答疑解惑,我的OO之旅非常美满,也是时候说再见了。
五.给课程提三个具体改进建议
首先感谢课程组的辛勤付出,每个学生对于OO课程的进步都是看在眼里的,当然这背后是助教和老师们的无私奉献。以下三点建议,第一点是我以高工学委的身份给出的,第二、三点是我结合自身体验给出的。
Ⅰ.第一点
17JXB06教学班在大二下学期课业压力过大,新执行的17级培养方案遭到了大量学生以及多名教师的反对,但大多数人最后还是挺过来了。最严重的问题在于,考期的任务量太重,可以考虑把Project作业前移,把博客类作业后移。此外,今年新入学的士谔书院学生入住学院路,高工在大三搬去学院路,可以考虑让OO重回大三。OO这门课可能是整个计算机课程体系中对代码能力锻炼最大的课,对于我系同学,由于还要修概统、基础物理实验(2)、数学建模等课,难免会存在学艺不精、测试不充分等情况,希望课程组与学院积极配合,给学弟学妹们更好的课程体验。
Ⅱ.第二点
UML单元可以改成用starUML绘图和书面分析的作业,或者另外添加该方面的内容。仅仅靠分析代码和实验课,可能仍然无法准确理解UML模型化设计。
III.第三点
互测可以考虑缩短冷却时间(当然这对评测机也是个考验),比如缩短到5分钟,与此同时限制hack次数,比如每个人最多15次机会。这样既可以提高hack质量,也能互测改善体验。