忆苦思甜——BUAA_OO_2021_第四单元总结

忆苦思甜——BUAA_OO_2021_第四单元总结

在这第四次作业中,我没有仔细设计架构,没有遵循OO的Solid原则,没有写数据生成器,甚至连自己的代码都没有完整认真地做基于阅读的静态检查。我想看看假如没有OO的知识,我能写出怎样的代码。

一、本单元作业的架构设计

Uml

Uml本身是面向对象Models的一个抽象,又叫MetaModels,而这种抽象竟然又可以用Models来进行完全的表示和运算,不禁让我想到了什么图灵完备,同构映射,本体论等等名词。我们要以联系和发展的眼光看待问题,就要总结出Models层元模型有什么元素和各种元素之间的关系,然后同构映射到UmlElements之中,并用一些数据结构将他们统筹起来,然后再基于数据结构来查询。

架构设计

程序的基本思想是还是采用了先建立结构再基于结构查询的方法,和第一单元建立表达式树差不多的感觉。

不过这单元由于官方提供了输入输出接口直接传进来了一些对象,我想为某些对象包装一些新的字段来储存关系信息,比如UmlClass类增加一些字段记录诸如继承,实现,参数等等信息,提前建树的好处是可以保证在建树和查询的时候都是O(n)的复杂度,且代码量差不太多。

在包装方面,有三种合理的方式:

一种是新建类继承UmlClass类,然后进行拷贝构造。

第二种是新类聚合旧类,并服从UmlElement的接口UmlElementInterface

第三种是自己新建接口,为所有的旧类都创建新类并实现自己的接口。

以上三种实现均是符合里氏替换原则的做法,但我偏不这样,因为这三种做法都会导致我写许多不必要的代码,我偏要懒一波,写出糟糕的代码。

对于需要新加字段的类如UmlClass,我直接写了MyUmlClass类,把旧类聚合起来,不进行任何继承和接口实现。对不需要新加字段的类如UmlAttribute就啥也不动了!

后来我结结实实的为自己的这个决定付出代价。

首先,由于UmlAttribute和MyUmlClass没有任何公共的父类或接口,我只能用Object[]类型的容器来储存它们。取出的时候便需要用大量的instanceof类型判断并混合使用switch-case语句来判断没有被我包装的类的类型,极其难看。

这个单元本身已经包括了许多不得不写的类型转化,空指针判断。而我这个类型不统一的架构则是雪上加霜。代码写一会就要判断一次类型,写一会就要为自己包装的类加点方法。我深刻地体会到了不仅工作量不减反增,而且编码过程中频频被打断,最终也是成功的写出了像那啥一样不可理解不可维护的代码。

这单元写东西极其烦躁,我直接破防。

数据的类型描述非常不清楚,就比如名字,没名字的定义究竟是null""还是"null"还是什么别的,其他数据是否可能是null,是"null", 是"",Uml元素parent都可能是哪些种类的Uml元素。什么叫lifeline的represent属性必须与一个在同一sequence diagram中定义的attribute的id对应。我们的数据结构里根本就没有sequence diagram类型的数据好不好?对应就对应,明明给定了UmlCollaboration这个数据类型,结果输入数据里却没有,搞得我一堆空指针错误。这是我的问题吗?

类图元素不能有名字,那么请问类图元素是什么,Attrubute算不算?哦若Attribute的parent是Class或者Interface就算,是Collaboration就不算,那parent找不着算什么?找到是别的类型算什么?就思考这个问题就无意义地浪费了我大量的时间。

我直接破防。我也不管了,爱错多少错多少,我可是懒得在这里再浪费时间了。

二、自己在四个单元中架构设计及OO方法理解的演进

第一单元--表达式求导

第一件意识到的事是面向对象中的类是封装数据和代码的手段,在代码量巨大的时候,数据不能再像C一样全用全局变量表示,名字起不过来,必须用封装来对抗复杂度。

第三次作业就体现了继承和多态,继承能让代码复用,多态能让多个不同的类表现出相同的性质,对外提供统一的调用接口,有效降低开发复杂度。

由第一次作业到第二次作业的迭代过程也让我意识到宁可多写一些代码来维持一个好的架构,也不要抠抠索索试图用最少的代码来完成任务。不然任何增量开发都会是实打实的增量,不光在代码量上占不到便宜,而且脑子也会晕晕乎乎,重构也会很麻烦。

第二单元--电梯

在这个单元我们学习了多线程开发方式,并学习了SOLID原则,在这个单元是我的编程技能突飞猛进的机遇。

利用SOLID原则开发出来的程序更加符合高内聚、低耦合、可复用性高等特点。我充分实践了SOLID原则,在第二次作业和第三次作业体验到了迭代开发的好处,节约了大量时间用来实现更加优化的策略,得到了不错的结果。

多线程并发编程虽然是第一次接触到,不过OO的实践辅助上OS多进程同步互斥的原理的讲解,我便完全理解了多线程的原理,并利用刚刚学到的知识开发了带有GUI的多线程高效率自动评测机,非常快乐!

第三单元--JML

这单元没什么架构,严谨的形式化表达看着还蛮舒服的,我对规格化的优点和弊端有了一定的认识。

第四单元--UML

最后一个单元我不仅没有用架构,而且还故意破坏了OO设计原则。人生的旅途不能总是一帆风顺,我想要更加直观的OO体会课为我带来的好处,就要体验失去它的样子。

实际上尽管第四单元本身不需要复杂的架构,代码都是基于简单的过程,失去OO的编程方法的我依旧过的很差。各种NullPointerException, ClassCastException让我到处debug。分析一下原因,一个是没有利用多态机制统一接口,让转换到处出错,二是高内聚低耦合的模块化做的不好,很难基于模块形式化论证正确性。

经过这第四单元我更加加深了对OO编程方法的好处的认识和理解。

三、测试理解与实践的演进

最开始,我对测试的理解都是基于这一则笑话:

一个测试工程师走进一家酒吧,要了一杯啤酒;
一个测试工程师走进一家酒吧,要了一杯咖啡;
一个测试工程师走进一家酒吧,要了0.7杯啤酒;
一个测试工程师走进一家酒吧,要了-1杯啤酒;
一个测试工程师走进一家酒吧,要了2^32杯啤酒;
一个测试工程师走进一家酒吧,要了一杯洗脚水;
一个测试工程师走进一家酒吧,要了一杯蜥蜴;
一个测试工程师走进一家酒吧,要了一份asdfQwer@24dg!&*(@;
一个测试工程师走进一家酒吧,什么也没要;
一个测试工程师走进一家酒吧,又走出去又从窗户进来又从后门出去从下水道钻进来;
一个测试工程师走进一家酒吧,又走出去又进来又出去又进来又出去,最后在外面把老板打了一顿;
一个测试工程师走进一家酒吧,要了一杯烫烫烫的锟斤拷;
一个测试工程师走进一家酒吧,要了NaN杯Null;
一个测试工程师冲进一家酒吧,要了500T啤酒咖啡洗脚水野猫狼牙棒奶茶;
一个测试工程师把酒吧拆了;
一个测试工程师化装成老板走进一家酒吧,要了500杯啤酒并且不付钱;
一万个测试工程师在酒吧门外呼啸而过;
一个测试工程师走进一家酒吧,要了一杯啤酒';DROP TABLE 酒吧;
测试工程师们满意地离开了酒吧。
然后一名顾客点了一份炒饭,酒吧炸了。

后来也了解了等价类的概念,对数据的分布进行了一些调整。

我的测试主要都分为三部分,全随机数据生成压力测试(检测一般的正确性bug)、极端构造数据(检测性能bug)、白盒测试,形式化论证代码的正确性。实践证明这三种方法都有作用,都可以发现别人程序的bug,也都能发现自己程序的bug。

四、课程的收获

  1. 理解了面向对象编程的封装继承多态的原理及好处。

  2. 理解多线程开发机制,学习了一点编程模式,学会运用SOLID原则。

  3. 学会规格化开发程序

  4. 从元认知角度重新审视面向对象编程,有更加清楚的大局认识。

五、改进建议

  1. 互测房建议显示自己成功刀的人的数量
  2. 第二单元互测增加压力测试模式
  3. JML单元压缩课时
  4. UML单元让表达更加形式化,减少诸如“符合Java8语法”,“可以在StarUML打开”这样的描述。
上一篇:go_["a","a","b","b","c","c","c"


下一篇:峰哥说技术:10-Spring Boot静态资源处理