BUAA OO 2022 Unit4 总结

BUAA OO 2022 Unit4 总结

本单元作业的架构设计

整体架构分成三部分:类图、状态图、顺序图

类图

自己封装的元素:MyUmlClass,MyUmlAttribute,MyUmlInterface,MyUmlOperation,MyUmlElement

内部分别要记录如下信息:

image-20220625095229100

 

拿MyUmlClass举例来说,在建模的时候,就将每个Class的父类、所有实现的父接口、继承的父类属性都更新好,这样在查询的时候可以直接O(1)方式获取到答案,不必遍历查找,节省了时间。而MyUmlInterface也如此处理。

需要说明一点,这种做法是将普通做法的一次查找的时间挪到了建模的时候。另外一种做法是使用记忆化搜索,即建模的时候仍然使用普通方法,查找的时候仅查找一次,当某个类的某个信息被查找过了,下次遍历需要该信息的时候可以直接获取。

状态图

自己封装的元素:MyUmlStateMachine,MyUmlState,MyUmlRegion,MyUmlTransition

做法同类图

顺序图

自己封装的元素:MyUmlLifeline,MyUmlInteraction

做法同类图

一些我认为值得分享的设计

分类

建立elementsClassification方法,将elements中的元素根据不同的类别进行分类,分别保存到不同的HashMap中,例如:

 private void elementsClassification() {
     for (UmlElement element : elements) {
         if (element.getElementType().equals(ElementType.UML_CLASS)) {
             allMyClasses.put(element.getId(), new MyUmlClass(element));
        } else if ....
    }
 }

这样做的好处是随后查找某个元素时可以直接在相应的类别中查找,不必遍历整个elements

名字查重

作业中经常需要判断某个元素的名字是否重复,或者某个元素的名字是否不存在等。针对这种无脑遍历的情况,可以提前将信息保存好,策略是用空间换时间

  1. 建立容器分别保存有效的名字和无效的名字(以Class为例)

     private final HashMap<String, String> validClassNames = new HashMap<>();    // name - id
     private final HashSet<String> duplicateClassNames = new HashSet<>();// duplicate classes' names

    validClassNames保存从name到id的对应关系

  2. 建立invalidxxxNameDetect方法在建树时处理好名字的检查

     private void invalidClassNameDetect() {
         for (Map.Entry<String, MyUmlClass> entry : allMyClasses.entrySet()) {
             String className = entry.getValue().getName();
             if (validClassNames.containsKey(className)) {
                 duplicateClassNames.add(className);
                 validClassNames.remove(className);
            } else {
                 validClassNames.put(className, entry.getKey());
            }
        }
     }

    这样,在duplicatexxxNames中保存着重复的类名,在validxxxNames中保存着有效的名字

  3. 建立checkxxxName方法在查询时进行名字的查询,同时处理好报错信息

     private void checkClassName(String className)
         throws ClassNotFoundException, ClassDuplicatedException {
         if (duplicateClassNames.contains(className)) {
             throw new ClassDuplicatedException(className);
        }
         if (!validClassNames.containsKey(className)) {
             throw new ClassNotFoundException(className);
        }
     }

方法名判断重复

方法名的判断上我借鉴了C语言中检查方法名重复的方法。将一个方法的类名、方法名、参数列表打包成一个toString

 public String toString() {
     parameterTypes.sort(Comparator.naturalOrder());//参数类型按照字典序排序,否则无法比较
     StringBuilder sb = new StringBuilder();
     for (String s : parameterTypes) {//这里只能包含参数方向为in的参数类型(如int,String或者自定义类型的id),不能有参数方向为return的参数类型
         sb.append(s);
    }
     return father.getName() + this.getName() + sb;
 }

自定义类型并且参数为referenceType需要特别注意:referenceId可能为有特殊意义的字符串(例如String,int),可以通过增加无意义的字符串避免

 public void addParameter(UmlParameter parameter) {
     if (parameter.getDirection().equals(Direction.IN)) {
        ...
         else if (parameter.getType() instanceof ReferenceType) {
             parameterTypes.add("FWordUOO" +
                        ((ReferenceType) parameter.getType()).getReferenceId() + "##");//增加无意义字符
        }
    }
    ...
 }

在四个单元中架构设计思维及OO方法理解的演进

第一单元——表达式

本单元主要任务是对输入的表达式进行逐层解析。由于表达式本身使用逐层形式化的定义,因此可以在文法的基础上建立递归解析的想法。我认为关键在于用对象来进行问题的解构。就像老师所说的,如果将每个类写好,它就可以像“被雇佣的程序员”来帮助你完成任务。只要是符合要求的数据,就可以交给它,让它完成。只需要保证输入的是正确的数据,就可以保证输出的是正确的内容。

此时类中封装了一些方法和属性,他就像一个小程序一样,把任务给你处理好了。

计算过程抽象图

 

第一次作业仅仅是简单的多项式化简,我采用了递归下降方法;第二次作业增加了sin、cos、函数和sum,我因为符号处理时机不对导致强测错了一个点;第三次作业增加内容很少。

第二单元——电梯

本单元的关键是保证线程安全,架构设计上主要使用生产者消费者模型。线程在各自运行的时候无所谓,可以玩了命的跑,但是如果需要访问临界区,就需要遵循一定的规则。我在这个单元中主要使用严格互斥,即临界区只能存在一个线程(可以采用读写锁,使得同步读,互斥写)。算法上采用经典的Look算法,也正如我在第二单元总结中所说,没有完美的算法。

img

 

  1. 关于重构,在本单元的作业中,我不像 Unit 1 一样抵触重构了。相反,我认为重构是一个让我们充分理解我们代码和整体结构的有效手段。hw5中我写完第一版后积极进行重构,有效缩减 Elevator 类 run 方法的行数,将电梯运行过程合理抽象整合。这个过程保证了我这三次作业电梯策略没有出现任何bug。另一个重构成果是将策略从共享对象(RequestQueue)中抽离,放到电梯类中何时的位置上,这个过程保证了我三次作业中访问共享对象没有出现任何问题。

第三单元——JML

本单元主要强调规格化。通过JML的描述,我在理解上不会出现歧义,这正是建模语言的妙处。正因为在理解代码上没有难点,所以只需要保证代码写的没问题即可。本单元的难点是使用算法,JML描述了一个基本的思路,但是在实现具体问题的时候毕竟还可以使用时间复杂度更低的算法和记忆化搜索,避免TLE

第四单元——UML

以UML中的类图、状态图、时序图为基础,进行查询,主要就是对于具体问题进行建模。和第一单元比较像,但是由于对UML比较熟悉,所以理解起来没有什么难度。在实现上只需要逐层对于模型进行解析,重要是清楚理解建模的顺序即可。

在四个单元中测试理解与实践的演进

需要从两个层次来理解整个OO课程的测试。

第一点,从测试的层次上,采用手动构建数据和自动生成数据相结合的方法。自动生成数据测试代码的覆盖能力,和大数据处理能力;手动构造数据测试极端数据和特殊数据的实现功能。第二点,从测试的过程来看,我采取合作的方法,我主要负责数据的测试和对拍(生成器也尝试弄过,主要根据wzk学长的无私奉献),其他同学主要负责数据的生成。

第一单元,在测试主要考虑replaceAll这些不可靠的数据解析方案。

第二单元,在测试时会考虑乘客生成的位置、方向、请求时间。前两次作业乘客主要是纵向方向,第三次作业涉及横向方向。但是手动构造时大同小异

第三单元,因为主要是考察图的算法,所以可以针对复杂度高的方法,针对性构造数据。第一点,就像刚刚提到的,随机生成的数据可能不能很好的保证覆盖到JML规定的所有行为,比如1111人和getReceivedMessages。此时就需要根据JML进行手动构造。第二点,根据随机生成的数据进行手动重新构造也是一种边界数据产生的途径。这种方法常见于前两个单元。随机数据的生成中对于某些侧重点的指令,比如qgvs,在测试的过程中可能就会出现TLE或者WA的问题,此时就可以通过分析随机数据生成的结果,手动构造针对性更强,强度更高的数据。

课程收获

  1. 学习到了面向对象设计方法,可以对大工程进行逐层分解,逐个击破

  2. 掌握多线程编写的基础方法和多线程学习的途径,不在惧怕多线程

  3. 代码能力提高,各路语言融汇贯通

三个建议

  1. 预习内容增加,提前(如寒假开始)就给出Java学习路径和方法

  2. 弱化性能分,毕竟是面向对象课程,不是算法课程,面向对象思路和多线程安全性是第一位的

  3. 电梯可以换成经典PV问题

  4. 讨论区可以使用github的issue形式,完善图形化,问题增加toDo、done等状态。提问和同学的帖子分开

  5. 增加同学们问问题格式和规范的要求

这里我提供一个网站,北大对于问问题提出了一些见解:

https://pku-minic.github.io/online-doc/#/preface/facing-problems

引用内容如下:

在完成编译实践, 乃至你日常编码的过程中, 你可能会遇到很多问题, 比如:

  • 不清楚某个工具的用法.

  • 不清楚某个功能实现的思路.

  • 编译/链接代码时出现警告或报错.

  • 运行程序时段错误.

  • 程序输出的结果和你的预期相去甚远.

  • 其他各种各样的问题……

与其说, 遇到问题是一件很正常的事, 不如说, 没遇到问题才是一件不正常的事情.

我们希望你在完成课程实践时, 不要:

  • 抱着应付的态度, 遇到问题时随便改改自己的代码, 能过测试用例就万岁了.

  • 发现随便改改也没用, 于是直接找大佬/助教/老师求助, 不加任何思考.

  • 求助的时候问一些相当没价值的问题, 比如: “我的程序出错了, 怎么办”.

    • 想象一下你的电脑小白朋友, 三天两头来问你 “我电脑坏了, 怎么办?”, 同时完全不说到底发生甚摸事了, 你还能否保持低血压的健康生活.

  • 在某些国内网站上一通乱搜, 找到了另一些国内网站上的低质量内容, 大抄特抄.

    • 然后丝毫没有意识到, 自己在几天后遇到的某个问题就是因为抄了垃圾代码而导致的.

  • 拖到最后才开始做课程实践, 发现自己做不完了, 疯狂求助大佬/助教/老师.

你应该:

  • 不放过任何一个问题, 包括编译代码时的警告.

  • 遇到问题时对背后的原因多加思考, 找出它的深层原因.

  • 独立解决问题, 同时不要怕尝试, 不要怕 debug, 你会在这个过程中学到相当多的东西.

  • 遇到自己解决不了的问题: STFW (Search The F… Fantastic Web), RTFM (Read The Fantastic Manual), RTFSC (Read The Fantastic Source Code). 搜索引擎和文档/手册里通常能找到绝大部分问题的答案, 如果没有, 源码里一定会有.

  • 学会摆脱对百度的依赖, 在 Google/Bing 上用英文搜索问题. 中文互联网什么样相信各位心中自有 B-tree.

提问的智慧

https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md

不要这样提问

https://github.com/tangx/Stop-Ask-Questions-The-Stupid-Ways/blob/master/README.md

posted on 2022-06-25 12:31  Zhang_kg  阅读(71)  评论(0编辑  收藏  举报