一、对面向对象的理解
有位同学给java的面向对象做了一个形象生动的类比,我觉得很有道理,大概按我的理解如下:
-
- 结构的形成
看图之前,我们要先明白,世界上是先有了实体,才有了一步步抽象至以上的体系结构,当然也未必是自底向上逐步抽象,也许在最初的认识体系中,只有故宫里的植物C、植物、和存在,或许迎客松A和蒹葭B都是植物的对象,在之后的认识中逐步向上抽象出生物,向下细分为树和草等等。
但无论如何,所有的抽象类都是我们从实体中归纳总结出的,不是凭空产生的。
在真实的程序设计中或许我们也是如此,也即先有简单的层次,生物-植物-ABC,随后逐步细化功能迭代开发。 - 类与接口
抽象类与接口十分相像,一般用借口能实现的东西我们都可以通过抽象类来实现,但从结构上看来,抽象类是分类,接口是功能,就像图中的光合作用是接口,树和草是类,树和草描述的是实体的构成模式,光合作用描述的是他们所具有的功能,还是有很大区别的。
- 抽象类
抽象类并不是不可以指代一个对象,仅仅是不能实例化一个对象,实例化的对象可以通过抽象类来指代,就像故宫里的那颗植物可能是一棵树,但是同样可以通过植物来指代。
- 结构的形成
二、实践与设计思路
至今为止面向对象开课以来已历五周,共实现了三次作业,都是表达式求导,功能逐步增加,对于面向对象理解的逐步加深也对我的程序结构产生了不同的影响,以下作出归纳:
- HomeWork1
题目描述:多项式求导,多项式仅由带符号整数、x的一次函数与x的幂函数构成。
对于面向对象一知半解,以为一个类用于当作函数主路口,另一个类用于实现功能就已经半只脚踩入面向对象的大门了;实际上不过是“include”一个头文件的过程式设计而已。当然功能实现没有问题,只不过到了HomeWork2需要重写了。且结构上维护困难。
由于Derivation类仅仅实现函数的入口功能,连格式判断都放在Poltnomia类中,所以直接的结果就是该类的体量巨大,度量数据超标都是此类的问题。
另一方面,由于正则表达式判断时(getIn方法)使用的是多个if-else-return结构,结构化程度ev(G)与循环复杂度v(G)都很大。但调试与理解起来并不是很令人费解,当然这是个人非数据的感觉。
总而言之,第一次作业的实现并没有明白面向对象程序的编写方式,用完全过程式的思想去编写程序,唯一觉的有利于编程的是java巨大方便的类库~
- HomeWork2
题目描述:多项式求导,多项式由带符号整数、x的一次函数、x的幂函数与x的sin、cos函数构成。
题目一出来,就发现第一次的作业白写了,于是有感为了让第三次的作业好写一些,尽力的修修补补得到了如下的结构。因为写的时候并没有题头的所述的那般理解清楚,所以很多结构上有冗余,第一次使用继承、抽象类,还没有很深的理解,于是勉强有如下的结构,但自认为结构上或不甚清晰,类内部的实现有些混乱。主要体现在AddFunction和MultyFunction的组成,使用了Function的Array组成其数据结构,但一方面这debug不方便,另一方面优化时不容易。
首先从类图看,Term是函数入口,Function作为求导函数的顶层抽象类,直接继承它的是加和函数、乘积函数和基本函数,基本函数也是抽象类,其有幂函数、X函数、常数函数和sin、cos函数五个子类。从结构实现上,基本实现了我所预想的结构。但静态分析仍有问题。
如上图,由于方法过长,右边的方法仅给出了超标部分的截图。
从类的结构来看,Term和Function的平均复杂度很高,这是由于Term沿用了正则表达式判断输入格式的方法,仍然是if-else-return加大了复杂度,Function是因为兼具了工厂函数的功能,并不仅仅作为抽象类而存在,我想这应该需要避免,功能和数据结构的定义最好分开。
从方法复杂度看两个match方法都是使用了正则表达式if-else-return的结构,加大了复杂度。而Multy中的getout主要原因是用多个if结构来优化造成的结果。
类间的相互依赖关系如上,因为MultyFunction和AddFunction中的函数项组成采用了顶层抽象类型,即内部类型结构表述与思考有些混乱,为了避免出现错误,就使用了最大的描述类型。这点在第三次作业做了些改变。
- HomeWork3
题目描述:多项式求导,多项式由带符号整数、x的一次函数、x的幂函数与x的sin、cos函数构成,允许sin、cos内部嵌套表达式及其他函数,允许幂函数底数使用x的函数项或表达式。
由于第二次作业的正确决策,第三次就不需要重新考虑结构,仅仅调整了幂函数的位置,并对结构内部进行了一些修改与优化,包括精确化函数类型,加和函数明确为乘积函数组成,乘积函数明确为幂函数组成,考虑嵌套,幂函数、三角函数内部使用加和函数类型。正则表达式判断格式直接使用了第二次的代码,基本没什么修改。另一方面,分离了工厂函数和顶层抽象类,使得结构更加清晰。
类图结构上并未有太大变化,入口函数在Derive类。
Derive类中包含了match正则表达式匹配方法,if-else-return结构使复杂度增大。当然,关于这个问题,可以通过递归分部解决,在递归部分我也加入了判断,或许程序中有冗余,但是并没有太在意去改变。CreatFunction类是生成函数,因为需要括号匹配,这一点或许可以通过递归逐层解决,但是在这次作业中,我使用的是过程式匹配,这或许有违面向对象的初衷。
由于结构的更改,Power作为优化中极为重要的一步,通过if来进行判断。复杂度略有增加。
另一方面,因为一开始写的时候并没有了解到instanceof可以判断函数具体到那个子类。所以有isBaseOr。。。来判断嵌套函数是否是常数函数或者表达式函数。
通过清晰化结构一定程度降低了类间的依赖度。
- 总结
三次作业的第一次作业完全按面向过程式编程,维护程度比较低下,程序测试分数也较低。第二次作业,第一次用了面向对象的思路编写程序,虽然测试分数更低了。。。当然或许第一次作业没有为第二次作业留下出了bug数据以为的好处。第三次作业很大程度上复用了第二次作业的代码,得到了不错的成绩,也算是一种鼓励吧~体会到了复用的好处。
三、bug分析
- 第一、二次作业
- 公测
- 互测
关于前两次互测,被发现的bug都是FormatWrong,正则表达式考虑不完全所造成的后果……因为通过正则表达式判别,或许与设计结构关联不大,一二次的结果相似。 - hack策略
通过自己程序的bug和复用曾经自己被hack的bug来测试别人的bug。效果不错。也有可能是因为身处C组的缘故。
查看别人的源代码,有针对的hack,成功率很高,但是效率比较低,看别人代码大多数时候真的很累。
- 第三次作业
- 公测
- 互测
因为不测WrongFormat!!!(如果测可能还是有问题)所以被hack的主要内容是关于常数项的判断,与前两次类似的地方在于,都出现在方法复杂度高的地方。 - hack策略
通过自己程序的bug和复用曾经自己被hack的bug来测试别人的bug。
查看别人的源代码,有针对的hack,未成功过。。。或许是水平不够。
四、总结与Applying Creational Pattern
大概收获最大的并不是某次作业,而是最后的一次同学分享。就如题头说的,面向对象的建构不是一蹴而就的,或许我们最先反应过来的模型都是简单的相对不抽象也不细化,得到的体系与结构也仅仅是不完善的。
表达式求导,在第二次采用面向对象方法设计的时候,确实在码代码之前进行了深入的思考,想清楚得得到一个清晰的架构,最后的结果是得到了一个初步的模型,但对于内部细节并没有很完善,具体的分析前文提到了。通过反思与和同学的交流,在第三次作业重写了部分方法,重新整理了数据结构,相对的跟清晰的获得了体系。当然任然存在在不完善的地方,通过反思与思考仍然可以更进一步的优化~
原文地址:https://www.cnblogs.com/YeSiyuan/p/10597658.html