也许是完完全全按照指导书写的流水账,也许是对前三周兵荒马乱的OO生活的总结,也许是对写代码的一点点思考.....
基于度量分析程序结构
OO_1(多项式加减)
第一次作业我设计了两个类,一个是Poly类,用来存储多项式的阶数和系数,并实现了多项式的加减方法。另一个是Main类,实现了正则表达式匹配,字符串分离提取和输出结果。在main方法中先判断表达式格式,然后实例化一个Poly对象,调用ParsePoly和ParseOp方法提取字符串中的数字和运算符,最后计算输出结果。
·度量:
·类图:
·优点:思路清晰,代码易读。
·缺点:大部分工作集中在Main类,Poly类所承担的任务较少。没有掌握好相关知识,不必要地使用静态变量,导致本次作业去掉了class之后就是一个巨大的面向过程式的程序。main中实现了太多功能,代码冗余。
OO_2(傻瓜式电梯)
第二次作业我设计了6个类。Request类判断输入要求的合法性正确性,如果错误做出相应的容错处理,如果正确则提取出参数;requestQ类初始化参数数组;dispatcher类实现调度计算。在main中构造一个floor类初始化楼层信息;再构造一个requestQ类的对象读入各行字符串,requestQ类中的input方法构造一个request类的对象判断字符串格式是否合法,若合法将返回的参数存入各个参数数组;接下来构造一个dispatcher类的对象,调用start方法,其中构造了一个elevator类的对象,根据指令输入顺序进行调度。
·度量
·类图
·优点:都是按照PPT上的规划写的(好像不是优点)。
·缺点:类之间的任务量分配不均衡,其中floor类没有起实质性的作用,调度器类里的功能只用一个方法实现,完全没有“面向对象”。程序中盲目使用static等修饰词。类之间的聚合度不够。
OO_3(ALS式电梯)
本次作业和上次的类数目,各类之间的调用关系一致,只是电梯的调度方法不一样。实现ALS调度的思路:遍历整个请求队列,若该请求未执行过,让它成为主请求把它放入优先队列中(以楼层排序)然后扫描其后的请求;如果其后没有请求了,直接执行。如果其后有请求r,要求r能被捎带,然后得到响应时间,若响应时间>请求产生时间,并且优先队列中原先没有该楼层的请求,则标记已经输出加入优先队列,若优先队列中原来有该楼层的请求但是不同质,加入优先队列,若同质,将指令标记为无效输出之后,加入优先队列。如果请求r成功加入了优先队列,那么重新遍历从主请求到r中间的所有请求,找出能够继续被添加进优先队列中的请求;否则,跳过。不断扫描,直到扫描结束。更新主请求,继续循环。
·度量
·类图
· 优点:减少了上次作业的代码冗余,将重复的代码段封装成方法,简洁了不少。
· 缺点:类之间的任务量分配不均衡,floor类不过10行,scheduler类的一个方法就要100+行。SC类对scheduler类的继承没有体现出继承机制的优越性,而是相当于重写了整个类。每个类里面大量的get..,set..方法,写的时候有愧疚感,好像上课一遍一遍强调不要写get和set的荣老师在盯着我,责备我似的。还是过于面向过程了。
Bug分析
·第一次作业:公测无bug,互测无bug。
·第二次作业:公测无bug,互测无bug。
·第三次作业:公测无bug,互测无bug。
但是!写的过程中还是有一大堆问题的,我们后面再说
找Bug的策略
首先就是用自己测过的样例(基本上是对分支树的覆盖测试)测一遍,然后看一下同学的正则表达式和格式错误时的判断思路,如果没有错用自己踩过坑的正常样例(通常非常大~)试一遍。
第一次拿到的小可爱公测只错了一个点,是因为正则爆栈了。互测没有找到bug。
第二次的大佬公测全对,但是如果第一条起始时间不为0输出ERROR后,后面的指令起始时间不为0不报错,直接正常运行。看了一下代码,应该是输入时只记录了输入的指令条数,没有记录合法的指令条数。
第三次的大哥乍一看公测,一半飘红,基本上捎带的全部出错。但是仔细看错的样例,捎带顺序都是对的,时间是错的,应该是捎带时没有计算开关门的时间。互测时我基本没有测捎带,我用了几个不能捎带的样例,发现他在电梯上行状态r.n<=e.n时错误地捎带了。如果他课下多测几个样例,这种时间算错的bug应该能轻松地找出来。
心得体会
1、第一次作业刚开始想用有限状态机,然后很可耻地知难而退了。周日晚上写完了正则,周一晚上完成了整个程序。周二发现用大一点的数据计算地会特别慢,查了一遍发现我把一个要遍历整个数组的方法重复调用了好多遍。本以为这次作业皆大欢喜了,结果离DDL三小时发现正则爆栈了。心态直接崩,赶紧上网各种查又问同学,把原来的正则分成内部和外部比较,终于交上了作业。
2、第二次作业明显难了,花了一天看PPT和指导书,基本了解了要实现那些类和哪些方法。格式检查和存数据部分和第一次没什么差别,很快就写好了。傻瓜调度也很简单,就是判断同质的时候费了一点功夫,为了代码简单我用了ArrayList,比数组容易操作而且不会溢出。
3、第三次作业乍一看非常容易,只用改一下调度方式。上手才知道太麻烦了,光捎带就分四种情况,这还是指导书明确说明了的,更可怕还有主请求和同质请求的一大堆循环判断。开始打算用数组实现,结果变量太多写到一半就晕了,所以改用优先队列。优先队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权限的元素。如果不提供Comparator接口的话,优先队列中元素默认按照自然顺序排列,也就是数字默认是最小的在队列头,字符串则按字典排序。如果想实现按照自己的意愿进行优先级排列的话,需要实现comparator接口。
优先队列大概长这个样子:
public class test { private String name; private int population; public test(String name, int population) { this.name = name; this.population = population; } public String getName() { return this.name; } public int getPopulation() { return this.population; } public String toString() { return getName() + " - " + getPopulation(); } public static void main(String args[]) { Comparator<test> OrderIsdn = new Comparator<test>(){ public int compare(test o1, test o2) { // TODO Auto-generated method stub int numbera = o1.getPopulation(); int numberb = o2.getPopulation(); if(numberb > numbera) { return 1; } else if(numberb<numbera) { return -1; } else { return 0; } } }; Queue<test> priorityQueue = new PriorityQueue<test>(11,OrderIsdn); } }
它可以自动根据捎带的优先级顺序将请求加入队列,在这次作业中真是帮了我大忙,当然大家可以自己实现一个优先队列。
这次作业遇到的最难忘的坑是,每次进捎带队列的时候,要再判断一下当前的同质情况!我最开始忘记判断导致同质请求虽然没有被执行,但是捎带时还是算了它的开门时间。开始构造的数据太弱了,根本没检查出来这个bug。
4、不要稍微有点思路就瞎写。第三次作业我把捎带想的太简单,第一遍写出来的东西不是不捎带就是进死循环。把需求弄清楚再写更节省时间。
5、要和同学多交流,加深对指导书的理解。
6、希望尽快学会调试,虽然print大法帮我解决了不少bug。