OO Unit1 总结
OO Unit1 总结
每次作业的思路和技术分析
No.1
一共写了8个类,2个接口,主要的其实只有4个类1个接口
主要接口:
- PowerFunction就是每一项去掉系数的那一部分,有求导和乘法两个方法;
4个主要的: - ItemMatcher负责将一行字符串解析;
- PolynomialInput负责从指定的InputStream一行一行读,然后利用ItemMatcher实例解析结果,创建多项式;
- SingleXPowerFunction实现了PowerFunction接口的类,表示只有一种未知数;
- Polynomial类,维护了一个map,PowerFunction为键,系数为值。
不重要的类:
- MainClass,包含main()和测试用例,单独弄出来方便测试;
- PatternMarks是enum类,方便管理正则表达式的标记;
- StringPatterns类专门用于正则表达式的生成,有一些静态的自动打包方法(添加捕获组、非捕获组、为捕获组设置标记);
- MixUsingDifferentPowerFunctionException类是自定义的异常,是一个RuntimeException,保证多项式类只能选择一种实现了Powerfunction的类做键;
- StringProcess接口是预处理的接口,实现一个String process(String)方法,会在读入的那个类,读入一行之后先对字符串进行预处理,再进行解析。
Metrics分析结果:
类名 | 平均圈复杂度 | 总圈复杂度 |
---|---|---|
Polynomial | 2.5 | 30.0 |
PolynomialInput | 2.0 | 14.0 |
SingleXPowerFunction | 1.625 | 13.0 |
ItemMatcher | 1.375 | 11.0 |
StringPatterns | 1.143 | 8.0 |
MixUsingDifferentPowerFunctionException | 1.0 | 4.0 |
MainClass | 2.0 | 2.0 |
PatternMarks | 0.0 | |
Total | 82.0 | |
Average | 1.745 | 10.25 |
复杂度较高的两个类Polynomial和PolynomialInput,确实都存在很大的问题。
- 对于Polymial,问题在于输出逻辑过于复杂,不仅要判断来自内层的PowerFunction的形式,还要判断本身的正负,导致一个输出需要三个函数。(还因此导致了第一次作业互测的大量bug)
- 对于PolynomialInput,问题主要是整体的设计。这个类需要将输入的InputStream作为参数,而本身以迭代器的方式向外输出一个个多项式。它利用ItemMatcher也只是解析Matcher,还要利用ItemMatcher返回的字符串数组信息来构建多项式。将输入一行字符串、拆分字符串、构建多项式全部耦合在一起了。
No.2
这一次作业我重新分析了整个多项式的数学性质,力求构建一个比较完备的多项式体系。经过分析发现多项式的运算有如下对应关系:
项*数 | 项+项 |
项^数 | 项*项 |
每行的一组运算都满足线性空间的运算要求的8条性质(第一列对应向量数乘,第二列对应向量加法),不过要保证这两个线性空间都有零元(而且是不一样的,前者是ZERO,后者是ONE)。其实数乘可以看作若干个加法,数幂可以看作若干个乘法。
构建一个比较完备的多项式系统的关键就是项的公共基类实现加法和乘法,我采用了如下策略:
- 利用Monomial实现【乘法】维护一个Map<项,指数>
- 利用Polynomial实现【加法】维护一个Map<项,系数>
Monomial和Polynomial也都是项类的子类。
这两个类都有化简方法,把Map中同类别的项拆开,只有一个key并且value为1时会退化为key,没有key时Monomial退化为ONE,Polynomial退化为ZERO。
实现运算的方法就是new一个相应对象,然后把被运算的对象一股脑放进Map中,再化简,然后上锁(不再允许更改)。
除了M和P两个类,还需要实现一些基础的类,项类理论上都最好是不可变对象,但是M和P一般创建的时候需要比较多的操作,所以给它们留了更改的接口,然后有一个加锁的方法(无解锁方法,加锁后尝试修改会引发RuntimeException)。
基础项包括:
- 字母类:维护一个字符,有一个静态对象x
- 常项类:ZERO、ONE两个静态对象
- 三角函数类:一个sin\cos的标记,一个内部项的引用
具体实现上,有这样的细节:
- 公共基类AbstractFunction是抽象类,实现了add(BigInteger),add(AbstractFunction),multiply(BigInteger),multiply(AbstractFunction),power(BigInteger)五个方法和为了方便的重载,这些方法都是返回一个新的对象。规定了一个抽象方法getDerivation求导;
- 由于Polynomial和Monomial在行为上表现极其相似(都是用于实现某个线性空间上的运算),所以其大部分行为都可以提炼出来成为一个抽象的AbstractMutableFunction类;
- 为了提高Polynomial和Monomial的安全性,为这两个类创建了static的工厂方法valueOf,创建并将所有待加入的项加入后,化简、上锁。注意工厂方法的返回值是AbstractFunction,因为化简可能会导致类型的改变(退化)。此外,将AbstractMutableFunction类所有可以改动map内数据的方法访问性改为protected;
- 注意Polynomial参与乘法运算的时候,输出结果其实是Polymial,这要求Monomial类化简的时候,若map内有Polynomial,化简结果应该是Polynomial。
构建结果如下:
除了多项式体系之外,输入和构建部分大规模重构:
- 弃用了原来的ItemMatcher(虽然没把它删掉);
- 把以前的PolynomialInput改为InputAnalyzer,它接收一行的String为参数,不再负责输入。它不再负责多项式的构建,将这个任务交给了FunctionFactory。它仅进行一行字符串的预处理,而这些预处理由StringProcess进行。
- 将以前的StringProcess接口更名为IStringProcess(其实是个C#命名风格),StringProcess改为一个实现了该接口的enum类,这里包括了检查格式、把原输入格式改为便于Factory处理的格式。通过InputAnalyzer+StringProcess的形式,使得预处理的执行和预处理的定义解耦,在后面要求更改后,只需在new的时候为InputAnalyzer指定其他的StringProcess组合即可,就像这样:
StringProcess[] processes = {
StringProcess.REMOVE_SPACE,
StringProcess.CHECK_FORMAT_TOTAL,
StringProcess.ADD_PLUS_AT_HEAD,
StringProcess.REPLACE_INDEX,
StringProcess.REPLACE_SYMBOL,
StringProcess.REPLACE_SYMBOL,
StringProcess.CHECK_FORMAT_POWER_LIMIT
};
InputAnalyzer analyzer = new InputAnalyzer(processes);
- 更改了MainClass,它现在会先尝试寻找一个testSample.txt,若找到了,进入测试模式,从该文件读入全部测试数据,将输出结果输出,并开始记录日志;否则,从控制台进行读入,输出也只输出到控制台。
Metrics分析结果:
类名 | 平均圈复杂度 | 总圈复杂度 |
---|---|---|
polynomial.FunctionFactory | 3.2 | 16.0 |
polynomial.functions.Polynomial | 2.455 | 27.0 |
polynomial.functions.Monomial | 2.417 | 29.0 |
polynomial.functions.AbstractMutableFunction | 2.167 | 39.0 |
polynomial.functions.AbstractImmutableFunction | 2.0 | 2.0 |
MainClass | 1.75 | 7.0 |
polynomial.functions.Factor | 1.625 | 13.0 |
polynomial.functions.IndexFunction | 1.556 | 14.0 |
polynomial.functions.AbstractFunction | 1.5 | 12.0 |
polynomial.InputAnalyzer | 1.5 | 3.0 |
polynomial.StringProcess | 1.429 | 10.0 |
polynomial.ItemMatcher | 1.375 | 11.0 |
polynomial.functions.SinFunction | 1.333 | 12.0 |
polynomial.StringPatterns | 1.1 | 11.0 |
polynomial.functions.ModifyAfterLockException | 1.0 | 4.0 |
polynomial.functions.Constant | 1.0 | 7.0 |
polynomial.WrongFormatException | 1.0 | 4.0 |
polynomial.PatternMarks | 0.0 | |
Total | 221.0 |
复杂度较高的几个类都是承载着核心逻辑的类。不过FunctionFactory确实过于复杂了,将各个部分拆成一步一步的函数有点随意了。
No.3
由于有了第二次作业中很完备的多项式体系,第三次作业很顺利,主要的更改来自于输入逻辑和化简逻辑。
多项式体系基本没变:
对于输入逻辑,由于有了递归的结构,所以不能一把正则进行格式检查,并且sin只能嵌套因子,不能嵌套表达式,这使得格式检查要分为表达式和因子两类。这样使得预处理和构建表达式都需要同样的嵌套顺序,所以现在将预处理和构建表达式合并在一起解决,废弃FunctionFactory,将其中的逻辑改到InputAnalyzer中。InputAnalyzer主要由四个方法:表达式的预处理、表达式解析、因子预处理、因子解析。两个解析方法都只能被预处理方法调用,预处理方法可以被随意调用,以实现嵌套的解析。
由于目前的多项式在做乘法的时候,默认是需要进行乘法分配律拆括号的,这将导致多项式长度过长,需要再进行公因式提取。现在的算法是进行穷举,穷举所有可能的公因式提取的可能,然后比较输出结果的长度,选取最短的结果进行输出。此时就发现了原有的多项式系统不利于这样的化简,因为公因式提取是以a*x**b为单位进行的,但是原来的体系把数乘和数幂分开的,所以需要引进新的体系。但是好在这个体系不需要做求导之类的事情,提取公因式的的时候也只操作最外层的Polynomial,所以很简单就能构成了。
Metrics分析结果:
类名 | 平均圈复杂度 | 总圈复杂度 |
---|---|---|
polynomial.functions.shortner.SquareSinChecker | 3.5 | 14.0 |
polynomial.FunctionFactory | 3.2 | 16.0 |
polynomial.functions.Monomial | 2.867 | 43.0 |
polynomial.InputAnalyzer | 2.857 | 20.0 |
polynomial.functions.shortner.CommonFactorFinder | 2.554 | 23.0 |
polynomial.StringProcess | 2.4 | 36.0 |
polynomial.functions.Polynomial | 2.316 | 44.0 |
polynomial.functions.shortner.CommonFactor | 2.25 | 18.0 |
polynomial.functions.AbstractMutableFunction | 2.211 | 42.0 |
polynomial.functions.shortner.Item | 1.818 | 20.0 |
polynomial.functions.IndexFunction | 1.8 | 18.0 |
MainClass | 1.8 | 9.0 |
polynomial.functions.SinFunction | 1.727 | 19.0 |
polynomial.OutputAnalyzer | 1.667 | 10.0 |
polynomial.functions.SingleFactor | 1.556 | 14.0 |
polynomial.functions.AbstractImmutableFunction | 1.5 | 3.0 |
polynomial.functions.shortner.Factor.ConstantFactor | 1.429 | 10.0 |
polynomial.functions.shortner.Factor.PowerFactor | 1.286 | 9.0 |
polynomial.functions.AbstractFunction | 1.125 | 9.0 |
polynomial.StringPatterns | 1.083 | 13.0 |
polynomial.functions.shortner.SuperFactor | 1.0 | 4.0 |
polynomial.functions.Constant | 1.0 | 10.0 |
polynomial.functions.ModifyAfterLockException | 1.0 | 4.0 |
polynomial.WrongFormatException | 1.0 | 4.0 |
polynomial.functions.shortner.Factor | 1.0 | 3.0 |
polynomial.PatternMarks | 0.0 | |
Total | 415.0 |
三角函数的化简因为ddl之前都没有修完bug,所以就没有使用了,从分析结果来看,其复杂度太高,也是bug没有修完的原因。因为它不仅要检查AbstractFunction的内部嵌套的类,并检查是否是sin/cos,这导致其与原来的Functions系统每个类的耦合都很高。
FunctionFactory已经被弃用了,其代替品InputAnalyzer尽管还肩负着预处理的任务,但其表现比它好得多。
CommonFactorFinder需要穷举全部公因式,所以和Polynomial、Monomial耦合都不低。
bug分析
这一单元我自己的bug数量控制的还可以,三次作业一共只有2个bug,一个是第一次作业没有关注输出0的情况,一个是第三次作业优化过程中陷入死循环了。这两个bug都是边界条件所致。
这一单元我主动hack别人的表现确实不佳,我觉得很大的原因是因为我没有构建自动测试系统,全靠人力测试,是在太慢了。但是我人肉发现的bug,很多都是边界条件,比如指数的边界、化简为0的情况等。第一次作业就是没有考虑化简结果为0的情况,导致被hack10多次(但全是这一个问题,所以一次就修复了)。以后可能需要构建测试机了。
面向过程→面向对象
第一次作业虽然有点面向对象的想法,但是实际上还是面向过程的偏多一些。第二次作业从线性代数的这是种寻找对象的行为和交互方式,先不管本次作业的主要逻辑(求导),先构造好类的体系,设计好它们的加减运算。最后再根据业务逻辑,利用这些构造好的体系完成业务(具体分析过程写在No.2中了,就不复制了)。
收获
- 总体结构要想明白了再敲键盘。由于第二次想明白了多项式的系统应该怎么构建,并且构造了一个足够完美的系统,使得,第三次在这一方面花费的精力大大减少(据我所知很多同学第三次作业都在重新构建这个系统花费了大量时间)。
- 算法也要想明白了再敲键盘。提取公因式的算法就是因为最开始没有想好,导致频繁陷入死循环(无法停止穷举)。后来仔细思考了穷举的算法,利用队列一个一个检查,分析了新的算法不会出现重复的情况。
- 从现实中的只是体系寻找类的行为方式、交互方式。这次作业我利用了线性代数的知识帮我构建了一个运算封闭的多项式系统,复杂、功能多,同时bug又少。
- 这辈子都没有过在这么短的时间内写这么多代码的经历。(上次这么多还是基物实验课的虚拟仿真实验的设计,但是这个写了好久呢)
OO Unit1 总结的更多相关文章
- 多项式求导系列——OO Unit1分析和总结
一.摘要 本文是BUAA OO课程Unit1在课程讲授.三次作业完成.自测和互测时发现的问题,以及倾听别人的思路分享所引起个人的一些思考的总结性博客.本文第二部分介绍三次作业的设计思路,主要以类图的形 ...
- OO unit1 summary
Unit 1 summary 一.前言 三周左右的学习,OO第一单元顺利结束了,个人认为有必要写个blog来反思总结一下自己第一单元的学习情况,以便更好地进行后面的学习. 之前从来没有写blog的习惯 ...
- OO第四单元总结暨期末总结
OO第四单元总结暨期末总结 目录 OO第四单元总结暨期末总结 第四单元三次作业架构与迭代 整体感受 HW1 HW2 HW3 四个单元架构设计与方法演进 Unit1 Unit2 Unit3 Unit4 ...
- 「BUAA OO Unit 4 HW16」第四单元总结与课程回顾
「BUAA OO Unit 4 HW16」第四单元总结与课程回顾 目录 「BUAA OO Unit 4 HW16」第四单元总结与课程回顾 Part 0 第四单元作业架构设计 架构设计概要 AppRun ...
- GLUT的简洁OO封装
毕业设计用到了OpenGL,由于不会用MFC和Win32API做窗口程序:自然选用了GLUT.GLUT很好用,就是每次写一堆Init,注册callback,觉得有点恶心,于是对他做了简单的OO封装.记 ...
- Atitit 基于sql编程语言的oo面向对象大规模应用解决方案attilax总结
Atitit 基于sql编程语言的oo面向对象大规模应用解决方案attilax总结 1. Sql语言应该得到更大的范围的应用,1 1.1. 在小型系统项目中,很适合存储过程写业务逻辑2 1.2. 大型 ...
- OO中,先有对象还是先有类?
就是问,在面向对象思想里,先有对象还是先有类,乍一看和先有鸡蛋还是先有鸡是一类问题,其实不然!这个问题,在lz考研复试的时候被面试官问过,一模一样,如今又在一个笔试题里看到了类似的题目,眨一下,有人会 ...
- OO方式下,ALV TREE和ALV GRID的不同之处
作为大部分报表程序的基础,ALV GRID差不多是每个ABAP开发者必须了解和掌握的内容,因此网上也不乏相关资料,而ALV TREE的应用相对较少,中文资料也就比较少见了.实际上,ALV TREE和A ...
- 从人类社会的角度看OO(独家视角)
引言 在OO的工作中,我们一定会涉及到类,抽象类和接口.那么类和抽象类以及接口到底扮演的什么角色? 本文主要是从人类社会的角度阐述类与抽象类以及接口的"社会"关系,从而让我们抛弃书 ...
随机推荐
- Ch1-What is DAX?
What is DAX? 数据分析表达式 (DAX) 是在 Analysis Services.Power BI 以及 Excel 中的 Power Pivot 使用的公式表达式语言.在第一版Powe ...
- 破解 Android 上 airpods 连接软件的pro版
0x00 起因 起因是在Android上用了一段时间的AndPods觉得不太好用之后,换到了另一个Play商店推荐的App.动画.连接和电量提示都用的很满意,就是每次连接的弹窗和APP里面都有广告,就 ...
- HDOJ-6628(dfs+第k字典序最小差异序列)
permutation 1 HDOJ-6628 这题使用的暴力深搜,在dfs里面直接从最小的差异开始枚举 注意这里的pre记录前一个数,并且最后答案需要减去排列中最小的数再加一 这里有一个技巧关于求第 ...
- Java 中为什么要设计包装类
尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...
- Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- FreeBSD ibus输入法框架配置
FreeBSD ibus输入法框架配置 ibus输入法框架配置.xinitrc中增加XIM=ibus; export XIMGTK_IM_MODULE=ibus; export GTK_IM_MODU ...
- javascript 之对象-13
对象 无序属性的集合,属性可以包含基本值.对象或者函数,简单理解为对象是若干属性的集合:我们常说的面向对象(oop)编程其实是指的一种编码的思想,简单理解为用对象来封装数据,利用封装.继承.多态对代码 ...
- POJ_2253 Frogger 【最短路变形】
一.题目 Frogger 二.分析 题意关键点就是那个青蛙距离.就是所有1到2的点的路径中,每条路径都可以确定一个最大值,这个最大值就是青蛙要跳的青蛙距离,然后要求这个青蛙距离最小值. 其实就是最短路 ...
- 数据搬运组件:基于Sqoop管理数据导入和导出
本文源码:GitHub || GitEE 一.Sqoop概述 Sqoop是一款开源的大数据组件,主要用来在Hadoop(Hive.HBase等)与传统的数据库(mysql.postgresql.ora ...
- Aibabelx-shop 大型微服务架构系列实战之技术选型
一.本项目涉及编程语言java,scala,python,涉及的技术如下: 1.微服务架构: springboot springcloud mybatisplus shiro 2.全文检索技术 sol ...