OO Unit 1 表达式求导

OO Unit 1 表达式求导 面向对象学习小结

前言

本博主要内容目录:

  1. 基于度量来分析⾃己的程序结构

    • 缺点反思
    • 重构想法
  2. 关于BUG

    • 自己程序出现过的BUG
    • 分析⾃己发现别人程序bug所采⽤的策略
  3. 测试集构造

    • 有条理地构造一个测试集
  4. 面向对象思维方式

    (次次重构,越来越像个OO代码,但还远远不够。


基于度量来分析⾃⼰的程序结构

前两次作业

第一次作业用两个ArrayList存系数和指数,第二次作业三元组,第三次作业才开始有点OO思维。

第三次作业

类图如下。

OO Unit 1 表达式求导

  • 思路:根据输入流面向过程地构造表达式树,再在树上求导,没有进行优化。
  • 主要结构:表达式树即一个项表,每个元素是一个项。项包含因子类和表达式类。
缺点反思
  • 类调用关系复杂

    根据类图可以看到类之间的耦合情况,发现表达式类与别的类之间的关系复杂。因为表达式既可以是整个式子,也可能是一个因子,因此,为了复用代码,直接把因子的处理和整个式子的等同。导致关系结构不是那么清晰。
  • 结构框架不OO

    对类的封装程度不够,函数子类里只有构造函数,求导过程没有封装到类中。
  • 继承没有发挥应有的作用

    类里只有构造函数,没有把别的属性、方法封装到各个类中,导致我的程序里继承的使用非常鸡肋。
关于重构

看了博客园里大佬们的架构,对自己的代码进行基于想象力的重构。

  • 正确封装各类

    把不同的函数设计不同的类,这我做到了。但是每个不同的函数类包含各自的方法、构造函数、对象,这点我做得不够。我把求导、get、set都放在一个父类中,并且分情况进行处理。

    正确方法应当是:对于子类中相同的方法、对象,可以放在父类中,而子类中不同的方法,应该封装到子类本身去实现。
  • 减小方法长度和复杂度

    部分复杂度较高的方法如下:

    OO Unit 1 表达式求导

    OO Unit 1 表达式求导

这个主要是因为把应该在子类中实现的方法都放到了父类中,分情况实现,所以导致父类的一些方法长。这个思路还是非常的面向过程。

因此,为了减小方法的复杂度和代码长度,正确的做法是:可以封装起来的函数、方法都放到外面去实现,在主函数中调用。不论是简单的get、set,还是复杂的求导过程,都可以封装起来。在主函数中,这些方法和函数就像一个个黑盒子。这样对于后续的debug、或者模块化测试也有很大的方便。


关于BUG

自己程序出现过的BUG
  • 输入流格式判断

    主要因为粗心。我没有用大正则,我的正则只是一个因子,然后判断是新的一项,还是应该合并到上一项中。自认为这样比较放心,因为正则太长怕出错。
  • 数据类型

    指数范围和底数的范围各有不同,要看清指导书的规定。
  • 正负号

    对+-符号的重判断,当代码比较复杂的时候,容易把功能写重复。在父类中已经做过的事,在调用时又做了一遍。写代码的时候思路要更清晰一些,更加有规划一些。这就是典型的没做好规划和设计直接上手写代码的后果。
  • 化简出错

    求导结果正确但在化简后表达式错误。这一般来说分为几种情况。

    第一,针对某种特定情况做了优化,但是条件(前提)划分不当,可能同样的条件,部分可优化,而有些不行。对此,正确做法是,正确精准地划分前提条件,再根据不同情况进行化简。

    第二,优化层次结构不清晰。在树上求导时,思路结构清晰层层递进,确保了正确性,而化简时也要有同样的清晰的架构。在第几层,哪几项,做什么类型的化简,至少要搞清楚这些问题才能动手。
发现别人bug采用的策略
  • 读代码,针对性地构造相应测试集

    读正则,然后发现漏洞。

    读一段多层嵌套的if-else,然后发现逻辑、情况分类上的错误。

    因为每个人对问题的思考方式、角度不一样,所以会采取不同的方式解决,那么在解决问题的时候不免会有疏漏。而这正是代码原作者本人很难发现的东西。
  • 自动生成测试集,炮火覆盖

    看别人写的代码,对于新手来说是一个非常好的学习途径。但没时间,所以自动测试,或者把我自己的测试集用脚本给每个人都跑一遍。然后matlab对比结果。这样偶尔能发现个别的bug。但这不够高效。

测试集构造

宗旨

覆盖性 每行代码都测到,每个分支都至少执行一次,判定条件都取到各种可能的值,各个条件的每一种组合都至少出现一次。

不怕麻烦 搞测试不如写bug那么畅快,测试需要全面周到,不能觉得无聊就觉得无意义而不好好做测试。

测试集构造经验
  1. 明确需求,细化到每个功能点
  2. 界定测试范围,找到边界值,往往是bug高发区
  3. 划分等价类,有效、无效(无效等价类检测程序对异常的处理能力)
  4. 构建因果图,检测不同输入组合。
  5. 错误推理,直觉中最可能出现错误的地方进行高密度针对性测试。
  6. 问小伙伴要他们的测试集。因为每个人对问题的理解不同,会关注到不一样的易错点。
构造测试集的时候我在想什么……

笔记本的备忘录上记了一条“构造测试集注意”,在构造测试的时候我每五到十分钟看一次,这条备忘录很短,我也不喜欢把很长的东西放到备忘录里。构造每一样例的时候想想,类似这个样例的不同样例还有没有。

备忘录全文抄录如下,其实也没分享的价值,它的作用在于时刻提醒我自己。

构造测试样例注意
1 多个,重复
2 广度。多少,前后,种类,不同条件
3 深度。多层,嵌套,里外

除此之外还在想,我的朋友们什么时候能把他们的测试样例发给我啊……

基于测试集写代码的建议

先写测试集,再写代码,似乎这样做比较小众,一般人都习惯于拿到题目(需求)就写,或者设计。但其实我觉得最好的还是先写测试集,尤其是表达式计算这种题,需要处理输入流格式的正确性的。先进行覆盖性的测试集构造,才能最好地设计如何检测输入流。

可能开始写代码的时间比别人滞后一些,但是debug的时间会短很多,而且对于代码的结构设计也更有条理了,不会重复判断。


面向对象思维方式

开始

我比较赞同一本书上的说法,这本书用童话的语言带我入坑,把OO思维讲得具有故事性,书名是《Head First JAVA》。刚开学我对OO完全没概念的时候,这本书告诉我对象、面向对象、方法都是什么。对象就是一句话里的名词,方法就是一句话里的动词,面向对象就是看着名词找它的动作,按图索骥。对于萌新本人来说,这样的解释比那些标准解释说明更有实操性。

抽象能力

面向过程编程的时候,我更看中逻辑、先后次序、顺藤摸瓜,给要做的事情放进to do list 的 FIFO。然后逐一把功能实现。

而面向对象的思维方式有点不同。

首先,抓住主要操作的对象实体是什么。然后分析要对它做哪些操作。最后才考虑先做什么,后做什么,怎样去做。我觉得这就很需要抽象的能力。对整个需求有一个明确清晰的把握,而不是从功能、步骤着手逐步地思考问题。

可能热爱“黑盒子模型”(?)也是从这儿开始。当抽象整体架构的时候,把如何去做都先简化成做什么,不能考虑太多怎样实现的问题。不然就会落入工程细节中,把握不准整体的运行。

学习其他构造模式

通过网上冲浪,我觉得工厂模式是一种从抽象到具体的构建模式。先有抽象类,或者接口,再根据不同的需求和情况构造具体实例。在后续的学习中,我会了解到很多关于抽象的方法和知识,我觉得从更高的层次理解问题,然后把问题抽象,是一种很重要的能力。

上一篇:判断List、Map、Set是否为空及效率比较


下一篇:C# 的TCP Socket (同步方式)