OO Unit 1 表达式求导 面向对象学习小结
前言
本博主要内容目录:
-
基于度量来分析⾃己的程序结构
- 缺点反思
- 重构想法
-
关于BUG
- 自己程序出现过的BUG
- 分析⾃己发现别人程序bug所采⽤的策略
-
测试集构造
- 有条理地构造一个测试集
面向对象思维方式
(次次重构,越来越像个OO代码,但还远远不够。
基于度量来分析⾃⼰的程序结构
前两次作业
第一次作业用两个ArrayList存系数和指数,第二次作业三元组,第三次作业才开始有点OO思维。
第三次作业
类图如下。
- 思路:根据输入流面向过程地构造表达式树,再在树上求导,没有进行优化。
- 主要结构:表达式树即一个项表,每个元素是一个项。项包含因子类和表达式类。
缺点反思
-
类调用关系复杂
根据类图可以看到类之间的耦合情况,发现表达式类与别的类之间的关系复杂。因为表达式既可以是整个式子,也可能是一个因子,因此,为了复用代码,直接把因子的处理和整个式子的等同。导致关系结构不是那么清晰。 -
结构框架不OO
对类的封装程度不够,函数子类里只有构造函数,求导过程没有封装到类中。 -
继承没有发挥应有的作用
类里只有构造函数,没有把别的属性、方法封装到各个类中,导致我的程序里继承的使用非常鸡肋。
关于重构
看了博客园里大佬们的架构,对自己的代码进行基于想象力的重构。
-
正确封装各类
把不同的函数设计不同的类,这我做到了。但是每个不同的函数类包含各自的方法、构造函数、对象,这点我做得不够。我把求导、get、set都放在一个父类中,并且分情况进行处理。
正确方法应当是:对于子类中相同的方法、对象,可以放在父类中,而子类中不同的方法,应该封装到子类本身去实现。 -
减小方法长度和复杂度
部分复杂度较高的方法如下:
这个主要是因为把应该在子类中实现的方法都放到了父类中,分情况实现,所以导致父类的一些方法长。这个思路还是非常的面向过程。
因此,为了减小方法的复杂度和代码长度,正确的做法是:可以封装起来的函数、方法都放到外面去实现,在主函数中调用。不论是简单的get、set,还是复杂的求导过程,都可以封装起来。在主函数中,这些方法和函数就像一个个黑盒子。这样对于后续的debug、或者模块化测试也有很大的方便。
关于BUG
自己程序出现过的BUG
- 输入流格式判断
主要因为粗心。我没有用大正则,我的正则只是一个因子,然后判断是新的一项,还是应该合并到上一项中。自认为这样比较放心,因为正则太长怕出错。 - 数据类型
指数范围和底数的范围各有不同,要看清指导书的规定。 - 正负号
对+-符号的重判断,当代码比较复杂的时候,容易把功能写重复。在父类中已经做过的事,在调用时又做了一遍。写代码的时候思路要更清晰一些,更加有规划一些。这就是典型的没做好规划和设计直接上手写代码的后果。 - 化简出错
求导结果正确但在化简后表达式错误。这一般来说分为几种情况。
第一,针对某种特定情况做了优化,但是条件(前提)划分不当,可能同样的条件,部分可优化,而有些不行。对此,正确做法是,正确精准地划分前提条件,再根据不同情况进行化简。
第二,优化层次结构不清晰。在树上求导时,思路结构清晰层层递进,确保了正确性,而化简时也要有同样的清晰的架构。在第几层,哪几项,做什么类型的化简,至少要搞清楚这些问题才能动手。
发现别人bug采用的策略
- 读代码,针对性地构造相应测试集
读正则,然后发现漏洞。
读一段多层嵌套的if-else,然后发现逻辑、情况分类上的错误。
因为每个人对问题的思考方式、角度不一样,所以会采取不同的方式解决,那么在解决问题的时候不免会有疏漏。而这正是代码原作者本人很难发现的东西。 - 自动生成测试集,炮火覆盖
看别人写的代码,对于新手来说是一个非常好的学习途径。但没时间,所以自动测试,或者把我自己的测试集用脚本给每个人都跑一遍。然后matlab对比结果。这样偶尔能发现个别的bug。但这不够高效。
测试集构造
宗旨
覆盖性 每行代码都测到,每个分支都至少执行一次,判定条件都取到各种可能的值,各个条件的每一种组合都至少出现一次。
不怕麻烦 搞测试不如写bug那么畅快,测试需要全面周到,不能觉得无聊就觉得无意义而不好好做测试。
测试集构造经验
- 明确需求,细化到每个功能点
- 界定测试范围,找到边界值,往往是bug高发区
- 划分等价类,有效、无效(无效等价类检测程序对异常的处理能力)
- 构建因果图,检测不同输入组合。
- 错误推理,直觉中最可能出现错误的地方进行高密度针对性测试。
- 问小伙伴要他们的测试集。因为每个人对问题的理解不同,会关注到不一样的易错点。
构造测试集的时候我在想什么……
笔记本的备忘录上记了一条“构造测试集注意”,在构造测试的时候我每五到十分钟看一次,这条备忘录很短,我也不喜欢把很长的东西放到备忘录里。构造每一样例的时候想想,类似这个样例的不同样例还有没有。
备忘录全文抄录如下,其实也没分享的价值,它的作用在于时刻提醒我自己。
构造测试样例注意
1 多个,重复
2 广度。多少,前后,种类,不同条件
3 深度。多层,嵌套,里外
除此之外还在想,我的朋友们什么时候能把他们的测试样例发给我啊……
基于测试集写代码的建议
先写测试集,再写代码,似乎这样做比较小众,一般人都习惯于拿到题目(需求)就写,或者设计。但其实我觉得最好的还是先写测试集,尤其是表达式计算这种题,需要处理输入流格式的正确性的。先进行覆盖性的测试集构造,才能最好地设计如何检测输入流。
可能开始写代码的时间比别人滞后一些,但是debug的时间会短很多,而且对于代码的结构设计也更有条理了,不会重复判断。
面向对象思维方式
开始
我比较赞同一本书上的说法,这本书用童话的语言带我入坑,把OO思维讲得具有故事性,书名是《Head First JAVA》。刚开学我对OO完全没概念的时候,这本书告诉我对象、面向对象、方法都是什么。对象就是一句话里的名词,方法就是一句话里的动词,面向对象就是看着名词找它的动作,按图索骥。对于萌新本人来说,这样的解释比那些标准解释说明更有实操性。
抽象能力
面向过程编程的时候,我更看中逻辑、先后次序、顺藤摸瓜,给要做的事情放进to do list 的 FIFO。然后逐一把功能实现。
而面向对象的思维方式有点不同。
首先,抓住主要操作的对象实体是什么。然后分析要对它做哪些操作。最后才考虑先做什么,后做什么,怎样去做。我觉得这就很需要抽象的能力。对整个需求有一个明确清晰的把握,而不是从功能、步骤着手逐步地思考问题。
可能热爱“黑盒子模型”(?)也是从这儿开始。当抽象整体架构的时候,把如何去做都先简化成做什么,不能考虑太多怎样实现的问题。不然就会落入工程细节中,把握不准整体的运行。
学习其他构造模式
通过网上冲浪,我觉得工厂模式是一种从抽象到具体的构建模式。先有抽象类,或者接口,再根据不同的需求和情况构造具体实例。在后续的学习中,我会了解到很多关于抽象的方法和知识,我觉得从更高的层次理解问题,然后把问题抽象,是一种很重要的能力。