OO2022第四单元总结

OO2022第四单元总结

以及课程总结

本单元架构设计

UML

unit4

MyUml*

MyUml*都直接或间接继承自MyUmlElement,每一个MyUml*持有一个Uml*的引用,为了重用和简化代码,提取了一些UML节点的共性,比如MyUmlInterfaceMyUmlClase的共同父类MyInterfaceOrClassMyUml*添加了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并没有讲到进程同步,所以如何同步只能靠自己摸索,除此之外如何对电梯的行为进行抽象也是一个问题,利用面向对象的思想,可以实现一个AbstractElevatorHorizontalElevatorVerticalElevator都继承于抽象的电梯,得益于这种实现方法,第二次加横向电梯相当于只是加入了横向的纵向电梯,可以重用大量代码。

面向过程编程基于函数(过程)实现了代码重用,面向对象更进一步,通过继承公共行为实现了重用。

第三单元

我们偷到了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对于函数式也是支持的)?

关于指导书

指导书普遍很长,难以阅读,很多时候读一遍完全不知道要实现什么,希望能够在保证准确性的基础上适当精炼。(这可能是对下一届助教提出的不切实际的要求)

posted @ 2022-06-23 22:26  aaicy64  阅读(15)  评论(0编辑  收藏  举报