OO2022第四单元总结
OO2022第四单元总结
以及课程总结
本单元架构设计
UML
MyUml*
MyUml*
都直接或间接继承自MyUmlElement
,每一个MyUml*
持有一个Uml*
的引用,为了重用和简化代码,提取了一些UML节点的共性,比如MyUmlInterface
和MyUmlClase
的共同父类MyInterfaceOrClass
。MyUml*
添加了Uml
结构信息,可以提高查询的效率。
比如Uml*
类一般只记录ParentId
,如果要从一个类找他的所有属性,直接通过Uml*
类需要遍历所有UmlElement
,所以在MyUmlInterfaceOrClass
中添加DupKeyMap<String, UmlAttribute> attributes
字段,记录其属性。
在输入UmlElement[]
并预处理后,构建了一个包含各UML元素之间关系,由MyUmlElement
构成的图结构。
为什么不直接继承,而使用组合?用MyUml*
直接继承对应Uml*
类并添加字段相比保存原来Uml*
的引用显然更简洁?
因为当我意识到这一点的时候已经写好了。。
DupKeyMap
对Map<T,List<U>>
的抽象,因为给出的UML中,元素名字经常不唯一,并且要进行相应查询,不能直接用Map
(一个名字可能有多个对象)。
这样写以简化实现,比如:
public MyUmlStateMachine getValidStateMachine(String s)
throws StateMachineNotFoundException, StateMachineDuplicatedException {
if (!myImplementation.getStateMachines().containKey(s)) {
throw new StateMachineNotFoundException(s);
}
if (myImplementation.getStateMachines().isDup(s)) {
throw new StateMachineDuplicatedException(s);
}
MyUmlStateMachine machine = myImplementation.getStateMachines().get(s).get(0);
return machine;
}
containKey()
:是否存在这个key;isDup()
:该key是否对应多个元素。
作业中有很多类似这种形式的查询。
查询都相对比较简单(指导书很复杂,在写这篇博客的时候第三次作业强测还没有出,我也不知道最终有没有bug或者是否符合要求),通过以各种方式遍历或者查找MyUmlElement
构成的图来实现。
关于CheckStyle
在写完后发现,如果把所有实现放在MyImplementation
里,文件行数会超。在这时可以使用IDEA方便的重构功能在不需要大量修改代码的情况下符合代码风格限制。
比如 重构>提取委托
可以把一些方法提取到一个单独的类里,呈现这种形式:
//MyImplementation.java
public void checkForUml001() throws UmlRule001Exception {
myUmlChecker.checkForUml001();
}
//MyUmlChecker.java
public class MyUmlChecker {
private static final Predicate<String> EMPTY_PATTERN =
Pattern.compile("^[ \\t]*$").asPredicate();
private final MyImplementation myImplementation;
public MyUmlChecker(MyImplementation myImplementation) {
this.myImplementation = myImplementation;
}
public void checkForUml001() throws UmlRule001Exception {
//Predicate<String> p = Pattern.compile("[ \t]*").asPredicate();
for (UmlElement e :
myImplementation.getElements()) {
if (e instanceof UmlClass ||
e instanceof UmlOperation ||
e instanceof UmlAttribute ||
e instanceof UmlParameter ||
e instanceof UmlInterface
) {
if (isNameEmpty(e.getName())) {
throw new UmlRule001Exception();
}
}
}
}
...
}
可以看到被委托的类持有MyImplementation
的引用,并实现了真正的逻辑,MyImplementation
只是调用MyUmlChecker
的方法。
有时候方法行数也会超,并可能发现有不少重复行,这时可以使用重构>提取方法
,并且能够方便地将所有重复替换为这个方法的调用。
对于禁止import *
形式导入,可以使用上下文操作
直接替换。
通过借助IDEA的功能,可以高效地调整代码风格。
关于架构设计思维及OO方法理解
第一单元
第一单元完成了表达式化简, 由于是正式学习面向对象编程的第一次作业(之前多少有些接触,但是更多的还是面向过程),实现的不是很好(至少不是很面向对象),比如存在一个巨大的包含一堆静态方法的simplifier
类。
虽然在之前写过Parser,想着应该比较好写,但是由于架构没有处理好,仍然花费了很多时间重构。扩展性也不太理想,第二次作业打了一堆补丁勉强能跑,第三次直接改不动了,只能重写。
之后的作业吸取了教训,就没有出现第一单元第三次作业那样彻底推翻重来的情况了。
第二单元
第二单元首次进行了多线程编程(其实每个单元接触到都是新东西),完成这次作业的时候OS并没有讲到进程同步,所以如何同步只能靠自己摸索,除此之外如何对电梯的行为进行抽象也是一个问题,利用面向对象的思想,可以实现一个AbstractElevator
,HorizontalElevator
,VerticalElevator
都继承于抽象的电梯,得益于这种实现方法,第二次加横向电梯相当于只是加入了横向的纵向电梯,可以重用大量代码。
面向过程编程基于函数(过程)实现了代码重用,面向对象更进一步,通过继承公共行为实现了重用。
第三单元
我们偷到了NASA核心代码的最后一页
)))))))))))))))))))))))))))))))))))))))))
第三单元学习了JML和契约式编程的思想,虽然在没有语法高亮支持的情况下看JML就像上面的笑话一样,但是JML的约束对于写出健壮的代码非常有帮助,同时理论上可以通过JML自动生成单元测试(虽然实际上工具不怎么完善)。JML对于明确需求(团队协作中)也有较大的帮助,这体现在第三单元和第四单元提问数量的显著差异上,第三单元的形式化定义是明确的,而第四单元自然语言无论如何描述总是感觉有歧义。通过形式化定义明确约定接口的实现,可以提高交流的效率。
第四单元
第四单元学习了UML并实现了UML的解析(似乎这才是传统的面向对象课程的内容,我们的OO课的内容要多很多),又是一种用于明确表述结构的建模语言,在开始编程前可以先画UML理清思路,虽然我们的课程作业还没有复杂到能够显著体现出UML的优势,但是如果项目规模扩大,用UML理清结构大概会很有帮助。
关于测试
测试只能发现错误,不能证明没有错误(也许第三单元的JML在向如何“证明没有错误”靠近)。
第一单元的测试主要靠手动构造数据,对各个部分进行有针对性的构造,但是“有针对性”主观性很强,覆盖率没有保证。
第二单元几乎没有办法测试,甚至bug都可能无法复现,测试方式退化到了瞪眼法加println
加上debugger
进行排错。
第三单元使用JUnit进行单元测试,由于有JML的约束,编写JUnit测试变得非常容易,配合随机数据生成器可以做到较完善的测试。
第四单元测试方法与第三单元类似,但是数据是StarUml画的,自动化程度不高,虽然理论上json可以方便地生成,但是UML本身规则比较复杂,随机生成器不太好实现。
课程收获
本学期为了完成OO作业花费了不少时间,整体上还是有不少收获,除了学习了面向对象地编程思想,通过每周的迭代开发,测试,互测,编码能力也有了比较大的提升。与此同时不局限于OO本身,进行了并发编程的实践,学习了JML和契约式编程的思想。
OO课内容非常丰富,虽然学习的过程比较痛苦,但是总还是有所收获,还是非常值得的。
一些建议
关于教学使用的语言
虽然Java8的使用最为广泛,但是也是快8年前的版本了,缺乏一些能够极大提升编程体验的特性(比如类型推导,一长串重复的声明没有必要且丑),教学很少有兼容性,稳定性的要求,更新的标准还能让学生了解一些新的程序设计思想(新的特性必然是为了解决一些问题)。大概可以更新到用Java11或者Java17?
既然课程叫面向对象设计与构造而不是Java程序设计,甚至可以换C#,Ruby?
关于课程内容
虽然课程叫OO,但是实际上我们讲的远比OO多,而且本质上OO不是目的而是方法,是为了实现更易于维护,更健壮的软件而出现的,那么其实可以不局限于OO,来点FP(其实课程中已经有不少涉及到的内容,不如展开讲讲,而且OO和FP并不是对立的概念,Smalltalk对于函数式也是支持的)?
关于指导书
指导书普遍很长,难以阅读,很多时候读一遍完全不知道要实现什么,希望能够在保证准确性的基础上适当精炼。(这可能是对下一届助教提出的不切实际的要求)