OO unit4 summary

Unit4

一、第四单元作业的架构设计

第四单元个人认为主要是考察对于层次结构的理解,即如何理解并处理好UML图的树状结构组织,在理好层次之间以及层次内部的相互关系之后,就只剩下代码实现的问题了。但是不得不说,刚开始接触UML特别是对starUML不熟悉的时候,如果指导书说的不是太清楚,真的很难动手实现,比如说在第一次作业的时候,我就完全理解错了,导致难度飞升,一度怀疑老师所说的“简单”。虽然说相比JML是真的不太简单

由于主要架构课程组都已经做好了,所以我们基本不用构思什么架构,只需要做好层次化的组织就行了

第13次作业

这次作业只要求实现类图相关的存储与查询。

在数据的存储方面,考虑到不同层次的元素之间是有很强的关系的,常常需要进行特定的操作,比如对于class这个元素,查询指令中有查询与class相关联的需求以及查询他所实现的接口的需求,很自然的想到被关联的class实例和被实现的interface实例最好作为属性放进此class实例中,当然还需要提供一系列的查询操作。

因为查询操作众多,显然需要使用hashmap进行存储,刚好hashmap是一对一的查询使用起来非常舒服,这里如果出现有多个键相同但是值不同的话,说明存在异常(同名),再建一个name2Validhashmap,对每次查询时输入的键进行检查是否有效就好了,速度也非常快。

但是课程组提供的接口给出的是课程组写的类对应的实例,此时如果采用继承来做就不太好实例化自己写的类,因此我干脆把课程组给的实例封进了自己写的类中,虽然结构上有亿点丑陋,有一点不OO,但是因为到了烤漆,也就将就着用了,最终形成以下结构:(其他的元素也大都如此,把课程组给的实例封进自己写的类中,然后就能很方便地使用

public class MyClass {
    private UmlClass umlClass;
    
    private MyClass father = null;
    private MyClass topFather = null;
    private boolean updatedTopFather = false;
 	...
    private HashSet<MyInterface> interfaces = new HashSet<>();
    private String name;
    private ArrayList<MyClass> associations = new ArrayList<>();
    
    public MyClass(UmlClass umlClass) {
        this.umlClass = umlClass;
        name = umlClass.getName();
    }
    ...
    public void setTopFather(MyClass topFather) {
        this.topFather = topFather;
    }
    
    public MyClass getTopFather() {
        return topFather;
    }
    
    public void setUpdatedTopFather(boolean updatedTopFather) {
        this.updatedTopFather = updatedTopFather;
    }
    
    public boolean hasUpdatedTopFather() {
        return updatedTopFather;
    }
    ...
    
    public void addOperation(MyOperation operation) {
        operations.add(operation);
    }
    
    public HashSet<MyInterface> getInterfaces() {
        return interfaces;
    }
    ...
}

此外,由于第一次作业CPU使用时间限制为2s,其中有几个非常耗时的查询操作,查实现的接口,(这里还需要查接口的继承情况),查关联的类等。

考虑到实现的难度,我选择用递归来解决问题,同时采用记忆化加速。即将相关的操作提供一个flag,如果flag有效则直接返回对应的查询元素,若无效,则进行对应的递归查找,并更新相应的信息的存储。

异常处理方面,要特别注意异常抛出的顺序,大体按照从顶层向底层的顺序抛异常就好了。

最后,考虑到输入元素的顺序可能不确定,因此我选择把所有输入的元素先分类存储,然后按照合理的顺序一个一个地存进相应的类中,为了方便管理,我写了一个MyinfoLoader的类来专门处理读入信息的问题。

OO unit4 summary

第14次作业

第二次作业相比第一次多了时序图和状态图的处理,处理过程完全一样,有了第一次的经验,这次作业做起来显得比较简单。同时CPU使用时间放宽到10s,再加上多的两个UML图的查询基本上没有什么耗时的查询操作,完全不需要什么算法,直接用暴力做法硬莽就是了。

由于新增了两种UML图,我新增加了两个模块进行管理:

OO unit4 summary

第15次作业

这次作业需要增加Rule的check操作,一共八条,可能是因为在烤漆,整体实现难度不太高

Rule3Rule4要求接口不能重复继承接口,类不能重复继承类,类不能重复实现接口。

由于在我的架构中,MyinfoLoader在读取信息的时候需要将实现的类或者继承的类(接口)存进属性,只需要对于第一次作业信息读取存储的部分加一个判断,并提供一个flag,如果存储信息的过程中出现了不合法的行为,那就直接把对应的flag置为有效,检查的时候查对应的flag就行了

比如在Rule4的检查中:

OO unit4 summary

如果没有update并且父类不为空,这时候更新他的继承的接口首先需要递归地更新他父类的接口,然后检查一下父类是否合法(如果父类标志位有效,子类置标志位)

再对父类实现的接口做一遍更新,同时如果接口标志位有效(不合法)那子类肯定不合法,置标志位。

最后把所有的接口合在一起去重,看是否有重复的,如果有表明需要置标志位,无效。

check模块中只需要对每个类更新一下,然后查一下标志位就好了:

public void checkForUml003() throws UmlRule003Exception {
        HashMap<String,MyInterface> id2Interface = myUmlClassModelInteraction.getId2Interface();
        Iterator iterator = id2Interface.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String,MyInterface> entry = (Map.Entry)iterator.next();
            MyInterface myInterface = entry.getValue();
            myUmlClassModelInteraction.updateFatherInterface(myInterface);
        }
        iterator = id2Interface.entrySet().iterator();
        HashSet<UmlInterface> ans = new HashSet<>();
        while (iterator.hasNext()) {
            Map.Entry<String,MyInterface> entry = (Map.Entry)iterator.next();
            MyInterface myInterface = entry.getValue();
            if (myInterface.getDup() == true) {
                ans.add(myInterface.getUmlInterface());
            }
        }
        if (!ans.isEmpty()) {
            throw new UmlRule003Exception(ans);
        }
    }

Rule2 有里面关于类和接口是否成环的问题,因为CPU时间十分充裕,直接用暴力做法就好,需要注意的是,对于已经遍历过的点需要打上标记,否则将导致TLE

OO unit4 summary

关于类的成环问题,由于只存在单继承,最简单的做法就是一直查父类,看是否能回到开始的类,如果能,说明类的继承会成环

需要注意的是,当所查的类在一个环之外的时候,会出现死循环,所以还得记录查找路径,当再次出现记录的路径里的类的时候应该结束查找。

接口成环我采用了bfs如果会回到最初的接口表示成环,注意为查过的点打上flag

二、四个单元中架构设计以及OO方法的演进

  • 第一单元:主要是多项式求导的相关问题,重点在于理解类的继承以及对于面向对象思维的初步理解,由于刚开始基本没有面向对象的思维,做出来的架构很差,重构一次之后用二叉树做出了自认为扩展性很好很棒的架构,但是一到化简就头皮发麻了,之后听到老师说不一定是二叉树,可以是多叉树(其实我感觉二叉树相比多叉树更具有面向对象味),然后又做了一次重构,达到了比较好的效果。我的基本架构就是不同的函数虽然求导的具体做法不同,但是都可以抽象为项,然后项和多项式之间可以相互套娃,都有求导接口,对顶层求导,会将求导这个方法一层层分发下去,而底层有不同的具体实现。
  • 第二单元: 主要是关于多线程的理解,关于多线程代码的编写与调试,关于生产者消费者以及其他部分设计模式的理解。在第一次写的时候由于没有考虑清楚细节,对于死锁的认识也不清晰,遇到了各种各样的bug,一团乱麻,再加上对于多线程debug也不熟悉,干脆推了重写,由于第一次作业架构还可以,后两次作业就直接把前面的架构包一下就能直接用了。总的来说就是需要分层次,该电梯管的就电梯管,该调度器管的就调度器管,不要有“长臂现象”,否则容易造成死锁,并且效率损失严重!
  • 第三单元:主要是关于JML语言的理解与实现,把规格这个概念建立在我们脑海中。虽然写起来一长串一长串的(课程组写ಥ_ಥ )但是对于理解确实很友好,不会产生二义性。本单元是真的可以说没有任何架构,把指导书看好然后对着课程组的代码实现相关的函数就完事了,唯一需要注意的就是方法的复杂的,所以会涉及到一些算法的问题。
  • 第四单元:主要是关于UML图的理解,训练的是层次化模块化的能力。首先需要通过starUML理解好作业中的UML图(类图,时序图,状态图)的相关概念与树状结构,然后自己构造相应的类提供相应的存储与查找方法进行查询。相比第三单元多了些架构设计,但没有多很多,做好层次化管理就好。

三、四个单元中测试理解

  • 第一单元:第一次自己动手写评测机,对于个方面都不太熟悉,加上第一单元我并不是很会递归下降,不仅作业中读取很费力,评测机的数据生成也很费力,即使勉勉强强生成了数据强度也还是不高,导致第一单元作业中有很多bug泪目,具体来说我的做法就是通过正则表达式解析逆向生成数据,然后跑出结果,然后与正确结果对拍,正确结果来自Python里的sympy……掌握了评测机的主要搭建思路:

    (构造数据->用命令行跑代码并记录结果->与其他结果对拍并记录对拍结果)为后面的评测机书写打好了基础。

  • 第二单元:在Python中构造电梯类,生成数据之后,跑代码,每输出一个提示信息,就读取并解析,更新Python中电梯类的相关状态并检查,一旦不合规范就相应的错误

  • 第三单元:随机生成各种数据(强度可能不高,用数量保证质量),然后找几个同学一起跑并对拍;专门构造一些容易超时的针对性数据,用time库来测运行时间,精度很高

  • 第四单元:先自己构造各种边界数据,考虑到一些基本情况,进行手动人工测试,确保程序大致正确。然后手工生成针对性数据,多个同学一起跑并对拍。

四、课程收获

一学期的OO课终于结束啦!总结了一下,大概有一下收获:

  • 面向对象思想的初步建立,无论什么好思想都是深邃的,思想的建立不可能一蹴而就,面向对象编程思想的初步建立将自己领进了这个领域的大门,为之后自己的探索指了一条明路
  • 码量的飞跃,一学期算下来作业(含重构)加评测机大概也有6000~7000行了吧
  • Java语言的熟悉,想要熟练掌握一门语言最好的方式就是多用它编写代码。此外踩了许多奇怪的坑,积累了经验
  • 评测机编写能力大大提升
  • 学会了许多工具的使用,特别是git,对于码量较大的项目是必不可少的东西,以及IDEA里各种大幅提升体验的插件
  • 了解并掌握了相关的设计模式
  • 建立了相关概念,例如:多线程,JML,UML
  • 磨砺了心智

五、具体改进建议

  • 实验课最好能公布答案,不然自己根本不知道做对没,上机几乎等于无效上机,实在想不到不公布答案的理由
  • 指导书中某些写的不清楚的地方希望能写的更清楚一点,特别是第四单元,看完指导书真的是一头雾水
  • 希望在每个单元结束后能展示一部分优秀代码,因为真的觉得自己有的地方写得很丑陋
  • 希望在互测中刀人之后能刀中了谁的反馈,这样就不会有漏网之鱼了,同时也能减少恶意Hack的情况
  • 希望研讨课的质量能更高一些,最好定几个ddl,做完PPT后助教要进行审核,对于质量不合格的打回重做,仍旧不合格将取消本次研讨分享资格
  • 讨论开通搜索功能,并开通标记功能(对于有的帖子以及看过了,可以标记为看过,避免浪费时间)
上一篇:BUAA_OO_2020_Unit4&Final_Summary


下一篇:8、线性回归模型原理与实现(三)——增加损失值等变量的显示