BUAA-OO-2021 第一单元总结
在上这个课前的一点点理解
编程范式
OOP(Object Oriented Programming)是一种编程范式。许多号称多范式的编程语言都有对面向对象的支持。
OOP的流派
其中C++的面向对象允许多继承、多态、以模板为特征的泛型化;Objective-C单继承、以传递消息实现函数调用、动态绑定、重载不允许同名而参数个数相同参数类型不同。前者为Simula67学派,后者为Smalltalk学派。OC可以一定程度上与Java类似地有动态反射能力(C++、Dlang等其它语言的编译期反射不计)。
AOP
AOP(Aspect Oriented Programming)可以视为OOP的延续。面向切面编程可以隔离降低业务逻辑的耦合性,提高可重用性。通过代理程序织入代码,类似python的装饰器,但对性能有少许影响。
选用Java在课程中的优点
Java的GC很适合让学生专心到OOP当中而非繁杂的内存操控(诸如内存分配、回收、锁、内存对齐、内存定域性、内存碎片、内存扩散等),对比如C++的Allocator、pmr抑或是shared_ptr、unique_ptr来说可以从底层中解脱出来。常用的JVM(hotspotVM或openj9VM)对此方面有较为全面的工作,包括JIT等都对于刚接触编程的学生来说性能较为友好。而C#的CLI(CLR或Mono)则略逊于它,乃至Stream API和LINQ的性能,C#的移植性也是最近才达成的(.Net 5),故抛开语言的优雅Java可以说更适合本次教学。第三方库在本次课程中不计,则按下不表。
关于编程原则
对象是实体,类则为对象的抽象。OOP拥有三大核心特性继承性、封装性、多态性。而一些后起之秀如Rust之流,以trait的类型系统为主而非传统的类与对象,亦能达成一些面向对象的特性(封装)。但作为一种思想,究竟是更清晰还是更啰嗦,需要程序员自主的判断。同时在写代码时,也更注重于“对修改封闭,对扩展开放”,以及DRY原则等。
第一次作业
类图
第一次作业比较简单,于是直接使用过程式的方式更为直观,提交的代码只有一个文件、低于100行(包括空行)。
代码度量
(注:ev(G)表示方法的结构化程度。iv(G)表示方法的调用紧密程度。v(G)表示圈复杂度。OCavg代表类的平均循环复杂度。)
-
代码统计
Source File Total Lines Source Code Lines Source Code Lines[%] Comment Lines Comment Lines[%] Blank Lines Blank Lines[%] Main.java 98 90 92% 0 0% 8 8% Total: 98 90 92% 0 0% 8 8% -
类复杂度
class OCavg OCmax WMC Main 5.20 9 26 Main.Pair 1.00 1 1 Total 27 Average 4.50 5.00 13.50 -
方法复杂度
method CogC ev(G) iv(G) v(G) Main.consumeExpr() 5 1 3 4 Main.consumeFactor() 4 2 4 5 Main.consumeItem() 3 1 3 4 Main.diff(Entry<BigInteger,BigInteger>) 9 1 5 5 Main.main(String[]) 24 1 7 9 Main.Pair.Pair(BigInteger,BigInteger) 0 1 1 1 Total 45 7 23 28 Average 7.50 1.17 3.83 4.67
其中Main中的OCavg较高是因为在判定token时连续的equals所致,因为在checkstyle允许下这样会比switch块的代码更短。
BUG分析
本次作业暂无BUG。
互测分析
自己所写的对拍程序没有找到同房间中程序的错误。后来猜测可能有对指数大小未用BigInteger的可能,故通过手写测试数据与查看源代码的方式确认了该BUG于房间中存在。
第二次作业
类图
第二次作业实际上已经接近第三次作业的架构。对于FuncWrapper有一个抽象类,其中操作、变量x、常量继承之,而操作又派生出一目运算、二目运算(指接受的参数个数)。此时已拥有了完整的功能,但对简化部分还较为薄弱。于是在此基础上引入了FuncWrapperHelper用于激进的化简,增加了诸如合并同类项、重排等功能。同时将取负操作删除,取而代之的是乘以-1,以得更简洁的表示。
另一个亮点在于本项目不使用简单的toString,而改为getString,返回一个由Stream<String>类似链表连接起来的字符串流,将生成字符串的时间复杂度从理论O(n^2)降为O(n)。
代码度量
-
代码统计
Source File Total Lines Source Code Lines Source Code Lines[%] Comment Lines Comment Lines[%] Blank Lines Blank Lines[%] Add.java 36 32 89% 0 0% 4 11% Cos.java 28 24 86% 0 0% 4 14% FuncWrapper.java 22 15 68% 0 0% 7 32% FuncWrapperHelper.java 174 167 96% 0 0% 7 4% Main.java 196 186 95% 0 0% 10 5% Mul.java 73 68 93% 0 0% 5 7% Num.java 59 46 78% 0 0% 13 22% OneOp.java 11 9 82% 0 0% 2 18% Op.java 4 4 100% 0 0% 0 0% PlusOrSub.java 6 5 83% 0 0% 1 17% Pow.java 78 71 91% 0 0% 7 9% Sin.java 38 32 84% 0 0% 6 16% Sub.java 38 34 89% 0 0% 4 11% TwoOp.java 17 14 82% 0 0% 3 18% Var.java 21 17 81% 0 0% 4 19% Total 801 724 90% 0 0% 77 10% -
类复杂度
class Ocavg OCmax WMC FuncWrapperHelper 5.83 10 35 Main 4.00 12 36 Mul 3.40 7 17 Pow 2.50 6 15 Sub 2.00 6 15 Add 1.75 4 7 Cos 1.25 2 5 Sin 1.17 2 7 FuncWrapper 1.00 1 3 Num 1.00 1 12 OneOp 1.00 1 2 Op 1.00 1 1 PlusOrSub 1.00 1 1 TwoOp 1.00 1 3 Var 1.00 1 4 Total 156 Average 2.23 3.60 10.40 -
方法复杂度
method CogC ev(G) iv(G) v(G) FuncWrapperHelper.simplifyExpr(PlusOrSub) 17 2 9 12 FuncWrapperHelper.simplifyItem(Mul) 17 6 8 13 FuncWrapperHelper.negativeFunc(FuncWrapper) 10 7 7 7 Main.parseExpr() 8 1 4 6 Mul.simplify() 8 7 5 9 Pow.getString() 7 5 6 9 ... ... ... ... ... Total 119 110 137 181 Average 1.70 1.57 1.96 2.59
通过复杂度的分析,FuncWrapperHelper中的代码复杂度偏高,因为对于化简使用了繁杂的逻辑,不适合人阅读与修改。而部分Mul类中亦显示复杂度较高,则是因为对于一些特殊情况的化简、生成字符串使用了专门的方法,增加了代码的复杂程度。
BUG分析
本次作业暂无BUG。
互测分析
又一次,自己所写的对拍程序未能发现错误。后来经过排查,又是数据范围给小了,并找到了一个披着BigInteger外衣而用着Long的程序,遂成功hack一次。但本房间其实还存在着另一个错误,本人在互测阶段没有发现。
第三次作业
类图
第三次作业的架构与第二次作业差别不大。在递归下降时添加了WRONG FORMAT的判断。同时额外引入了FactorHelper,本来是用来作有限域上因式分解的尝试,但因较为复杂故只有了展开括号的功能。
代码度量
-
代码统计
Source File Total Lines Source Code Lines Source Code Lines[%] Comment Lines Comment Lines[%] Blank Lines Blank Lines[%] Add.java 36 32 89% 0 0% 4 11% Cos.java 37 33 89% 0 0% 4 11% FactorHelper.java 41 41 100% 0 0% 0 0% FuncWrapper.java 36 26 72% 0 0% 10 28% FuncWrapperHelper.java 174 167 96% 0 0% 7 4% Main.java 226 216 96% 0 0% 10 4% Mul.java 73 68 93% 0 0% 5 7% Num.java 69 54 78% 0 0% 15 22% OneOp.java 11 9 82% 0 0% 2 18% Op.java 4 4 100% 0 0% 0 0% PlusOrSub.java 6 5 83% 0 0% 1 17% Pow.java 78 71 91% 0 0% 7 9% Sin.java 50 38 76% 6 12% 6 12% Sub.java 38 34 89% 0 0% 4 11% TwoOp.java 17 14 82% 0 0% 3 18% Var.java 21 17 81% 0 0% 4 19% Total 917 829 90% 6 1% 82 9% -
类复杂度
class Ocavg OCmax WMC FactorHelper 11.00 11 11 FuncWrapperHelper 5.83 10 35 Main 4.11 12 37 Mul 3.40 7 17 Pow 2.50 6 15 Sub 2.00 6 15 Add 1.75 4 7 Cos 1.25 2 5 Sin 1.33 3 8 FuncWrapper 1.00 1 6 Num 1.00 1 14 OneOp 1.00 1 2 Op 1.00 1 1 PlusOrSub 1.00 1 1 TwoOp 1.00 1 3 Var 1.00 1 4 Total 176 Average 2.32 4.19 11.00 -
方法复杂度
method CogC ev(G) iv(G) v(G) FactorHelper.expand(FuncWrapper) 27 10 9 12 FuncWrapperHelper.simplifyExpr(PlusOrSub) 17 2 9 12 FuncWrapperHelper.simplifyItem(Mul) 17 6 8 13 FuncWrapperHelper.negativeFunc(FuncWrapper) 10 7 7 7 Main.parseExpr() 9 1 5 7 Mul.simplify() 8 7 5 9 Pow.getString() 7 5 6 9 ... ... ... ... ... Total 156 128 163 209 Average 2.05 1.68 2.14 2.75
相比于第一次作业,代码长度因为优化的需求而膨胀较多。而代码复杂度当中FactorHelper一下子收获了更高的复杂度,这源于其会一一检查传入的类型instanceof哪一个种类,并且逻辑较为繁琐。理论上可以将expand添加到FuncWrapper基类中作为一种基础的操作,这将会使其复杂度均摊并使平均代码较短,但对修改封闭的原则故后期几乎没有修改作业二祖传下来的代码(也就是结果上来说越改越难维护了)。也就是对于架构上来说暂时并没有重构,但是会将累积的问题推到将来重构,这在完成项目时不是一个好的选择。而观察到部分的getString和simplify也被复杂度标红,则是因为不同的FuncWrapper互相之间的解耦不完全,需要花一些代码处理特例。
BUG分析
本次作业在强测中未出错。在互测阶段被检查出错误。
关于bug的唯心主义的原因,也在于最后的修改后并未对拍导致,过于心急地提交了。实际上在hack阶段本人也通过对拍找到了自己程序的bug。关于bug的代码层面的原因,在于出错的优化。其一,本程序中选用了若干种方法操作后结果的最短者,但是除了smartSimlify处理后以外的表达式并不一定满足作业三题面的要求;其二,本程序会检测Sin函数中的内容的结果是否带有前导的负号,并将其提到Sin的外侧,而如果此时Sin还被嵌套了幂函数则会出错(因为此时幂函数的底数变成了-1*Sin,而这不符合幂函数底数的要求);其三,因为幂函数底数非常规的情况依然存在(比如(x+1)**5),故本程序还有将非常规底数展开为连乘的功能,而此时如果遇到其二则是可能造成负指数,则会因此抛出异常,导致捕捉后输出WF,而这不是所预期的。
互测分析
通过对拍程序(针对作业三的),率先顺利地找到了自己的程序的错误。然后接着找到另外三个人的BUG,并成功hack了两位。
该对拍程序虽然在前两次互测中未发挥出特别大的用处,其实是因为生成数据的设置过于宽松,实际上其设置与功能比较全面。该程序用python实现并达7.89K,包含了各种类型的数据生成并能设定数据范围、嵌套深度、数据种类、概率分布等;用sympy进行判别并能处理多种异常,可以在超时的情况下终止运算;允许从多个源代码或jar运行受测程序;允许随机或手动的数据装填;可以对多个人进行统一评测等。
单元总结
第一单元的难度对入门来说刚刚好,熟悉了Java语法和基本的面向对象知识。
对于我个人来说:一个是给自己敲响警钟,不能过于轻视作业(从而导致了被hack),不可好高骛远,还是要实打实地完成课程。另一个是熟悉了IDEA的更多内容(如checkstyle、diagram等),然后与OS一同了解了更多git的操作,以及更多Java的实际编码操作。总的来说,还是希望自己能更多更广地学习,认真对待作业,多多与同学交流进步。

浙公网安备 33010602011771号