面向对象设计与构造第一次总结作业

第一次作业——多项式计算

---结构分析

  第一次作业我只使用了两个类,正像下面的类图所表示的那样,分别是Poly和ComputePoly。Poly类是不可变的,能保存一个多项式,可以进行加、减运算。ComputePoly是程序的主类,能够读取一个多项式加减运算表达式的字符串,并输出计算结果。parseExpression方法通过调用parsePoly方法和parseOperator方法将输入的字符串转换为Poly对象和运算符列表。compute方法从polyList和opList取出多项式和运算符进行计算,返回计算结果。

  总的代码量是248行(后面的度量分析图中可以看到),其中ComputePoly类占167行,Poly类81行;程序总共有28个方法,Poly占10个,而ComputePoly占18个。由于写Poly类的时候参考了教材的写法,Poly还算比较合适,无论是从两个类的代码量还是方法个数来看,ComputePoly都显得不太协调。事实上也如此,ComputePoly做了除计算外所有的事情:处理输入(包括错误处理,提取多项式),将多项式和运算符们存到数组里,调用compute方法得出结果,最后输出结果。这是典型的面向过程式的思路,颇有一点用C语言写程序的味道。

第一次作业类图

  再仔细一想,ComputePoly做这么多事情合适吗?就这个不那么复杂的程序而言,管理ComputePoly所做的事情还是能够接受的,但是再实现一个乘法功能呢?很明显,上面的两个类都需要做修改,对于Poly而言,只需增加一个计算乘法的方法就可以了,但是对ComputePoly来说,第1,4,5,6共4个方法都需要修改。假如按照建议设计那样将ComputePoly进一步分成3个类InputHandler,PolyManager,PolyArithmetic,同样地,输入处理InputHandler要做修改,PolyManager也需要修改,但PolyArithmetic不需要修改。实际该改的都得改,说白了还是前面所说的第1,4,5,6个方法。但是在这样一种设计下进行修改的复杂性就降低了,修改InputHandler时只需要记得正则表达式改一下能匹配乘号,而不需关心乘法和加法在一起时的谁先计算谁后计算的问题;而修改PolyManager时也无需关注输入是否处理好了,只用专心实现calculate方法和appendOperator方法(如下图)。这样一来不用在ComputePoly长长的代码中苦苦寻找某个方法,上改下改,减少出错的可能性,二来也不会被整体的复杂性所烦扰,分解成两个问题后可单独实现。

  无论如何,在初识面向对象设计前我还是带着面向过程式的思维,幸好能够从教材上学习到如何编写Poly类(非常感谢第一次作业时有这么好一个范例),初步领略了OO之美。下图是用eclipse的metrics插件对第一次作业代码分析得到的结果。重点关注一下第一个McCabe Cyclomatic Complexity,它中文名叫圈复杂度,是流程控制图中独立路径的数目,主要由分支和循环个数决定,越大表明越复杂,测试时所需考虑的情况也就越多(因为每个路径都要被测试),当它非常大的时候,程序的测试就变得十分复杂。这里的最大值是10,还能接受。仔细查看,造成最大值的方法是ComputePoly里的parsePoly方法,原因可能是需要进行输入检查,涉及的不同错误输入种类数较多。

   Nested Block Depth是if,while,for的嵌套深度。这里是最大值3,处于正常范围。最大值一个来自Poly的add方法,考虑到实现两个多项式加法的细节,是可以接受的,另一个同样来自parsePoly方法,原因同样是涉及输入错误处理。

  再看一下Method Lines of Code,方法的代码行数,这里最大值是26,不算太大。

第一次作业度量计算

---BUG分析

  此次作业的bug在于正则表达式的匹配,我使用一个正则表达式试着去匹配整个输入的字符串,潜在的危险是堆栈溢出。在分类树上是有“压力测试”这一项的,而且助教也说过注意不要爆栈,但是当时没想明白什么会导致栈溢出,而且是第一次使用正则表达式,主观上也认为压力测试没什么用,就没有构造相应的测试样例,导致了这个bug的产生。bug出现在isCorrectFormat方法中,这个方法检查了输入的字符串是否符合规定的格式。要解决这个问题可采用分段匹配的方式,整个字符串是多个多项式,中间用加减号连接,因此可以分别匹配每个多项式。

  测试同学代码时,我使用了每个分支树结点的对应测试用例(除了压力测试)。同学的代码中存在类似以“f”,“ff”,“fff”命名的变量,我尝试着去理解同学的意图,再加上大量的分支循环嵌套,实在是难以揣测。

第二次作业

---结构分析

  第二次作业一开始我花了一天的时间理解作业指导书,从头到尾读了好几遍才弄清楚。到第三次作业也是如此,我试着边读边用自己的语言去表达指导书的规定,并且举出例子,然后分条把觉得重要的点写在纸上,这样做有些笨拙和繁琐,不过的确能帮助我理解指导书的意图。

  课件中给出了提示,要构造电梯类、调度类、请求类、请求队列类和楼层类共5个类(如下图)。要怎样确定该哪个类该做什么,这是除了理解指导书外另一件头疼的事情。左思右想,苦思冥想,难以划分各个类的职责。另一个问题是调度器类的command和schedule方法,弄得我一头雾水,写完后都没能参透其中奥秘,直到看了互测同学活生生的代码,才恍然大悟。

 

第二次作业建议

  第二次作业的要点是如何把程序功能均衡地分配给各个类,如何让多个类之间协同工作,要避免出现Idiot Class和God Class。从下面的类图中可以看到Floor类就是比较白痴的一个类,它只知道楼层顶楼和底楼的编号。其实,可以考虑让楼层类知道更多的信息,比如某层楼是否有电梯到达。

  除了出现了一个Idiot Class外,另一个缺点是,在main方法里展开对输入的处理,这与第一次作业相同,由于自己没能意识到这种做法的坏处,在第二、三次作业时仍没有加以改正。

  本次作业的设计是否均衡呢?下面就再用定量的方法分析一下。 

 

第二次类图

  每个类的属性个数、方法个数、代码行数如下面的表格所示,其中方法个数包括了构造方法。从数据上看,方差较大。代码行数最多的类是ElevatorSystem,可能是因为在这个类里做了输入处理,如果把输入的处理分开来,应该会更均衡一些。

傻瓜电梯
 ElevatorElevatorSystem FloorRequest RequestQuue Scheduler 均值 方差 
属性个数 74 3.3 4.6
方法个数 14 2 4 9 10 6 7.5 19.1
类代码行数 95 107 18 48 38 55 60.21170.2 

    

  一个比较大的数据是电梯类的方法个数14,其中用于状态查询的方法占了一半。由于事先未规划好,在编码的时候为了方便,新增了一些方法。仔细分析会发现一些方法是冗余的,比如getStatus方法,事实上这个方法也从未被调用过。另一个原因可能是题目要求的电梯状态是定义在左开右闭区间上的,有时候为了方便我会使用左闭右开区间,这也增加了一定的复杂性。

  再看一下类的职责是否明确。

  拿电梯类举例,它总共有14个方法,方法总数占到整个程序约1/3,但仔细看,只发现能够让电梯改变状态的只有前两个方法readyToGotoFloor和run,run方法是让电梯运行到0.5s后的状态,而前者确定电梯的下一个目标。也就是说,别的类只能告诉电梯下一次去哪个楼层,电梯只管去,并且自己决定方向,其他类不能干涉电梯的运动方向。假设其他类能直接修改电梯的方向,那么在这个设计中,如果调度器让电梯向下走,但又是去往楼层数高的地方,这明显是不合适的。电梯内部不存在请求队列,无论何时,电梯都只有一个目标,它不用操心有多少请求在队列中等着它执行,只用听从调度类的指挥就可以了。

  从功能的角度上看,电梯的职责是明确而单一的。但是这样做的复杂性在于,电梯调度类需要精心地设计,在每次给电梯发送命令前,需要使用电梯类提供的一系列状态查询方法检查电梯状态(之所以要检查是因为调用readyToGoToFloor会立即改变电梯的运动状态,例如当电梯向上运行时,调用方法让电梯去往比当前楼层数小的楼层,运动方向就会突然改变)。因此,调度类必须充分了解电梯各个状态的含义(尽管它不需要了解电梯是怎样确定自己的状态的)和一些内部细节,否则就可能会导致电梯出故障。这就在一定程度上增加了电梯类与调度类的耦合性,一是使编码时复杂性增加,二来修改、新增功能时容易出错(例如我在写第三次作业的时候就在这上面犯了很多错误)。

   下图是第二次作业的度量分析结果。可以明显地看到标红的圈复杂度较高,最大值是17。进一步细看(图中未给出)可以发现高复杂度的来源主要是ElevatorSystem类的parseRequest方法和main方法,以及Elevator类的run方法。前者是由于输入的错误情况较多,个人写得比较凌乱,判断逻辑复杂,main方法里也做了输入的处理。后者是电梯运行时的逻辑稍微复杂,分支较多,也有两层嵌套的情况。

  再看一下每个方法的行数(图中最后一行),最大值是46,与第一次作业相比有所增加,其来源同样是parseRequest和main方法。如果将输入处理部分单独封装在一个类中,并且优化一下错误处理的逻辑,应该能使整体设计均衡一些。

第二次作业度量分析图

---BUG分析

  这次作业的bug是在时间很大时运行的时间较长,需要十几秒,这是由于实现采用了每0.5s进行一次操作的方式。

第三次作业

---结构分析

  本次作业在前一次作业的基础上增加了捎带功能,用继承的方式实现了ALSScheduler,对其他的类也做了一些调整。

 

第三次类图

  由于保留了第二次作业的大部分内容,本次作业在均衡性上没有改进,反而由于增加捎带功能后变差。尤其是ALSScheduler,代码行数最多,逻辑也较复杂。

稍微聪明一点的电梯
 ElevatorElevatorSystem FloorRequest RequestQuue Scheduler ALSScheduler均值 方差 
属性个数 8434 3.9 4.1
方法个数 17 2 4 12 11 512 9 29.3
类代码行数 108 108 18 59 48 55141 76.71857.9

  细心的读者可能会发现这次的圈复杂度下降了1,但这不是因为进行了优化,只是做了点微调。这里最大值16也不是前面提到的输入处理带来的,而是来自ALSScheduler的command方法。为了实现捎带功能,我增加了很多条件判断,既难写,又难以理解和修改。

第三次度量

---BUG分析

  1. 第一条请求没能够支持前导零和正号。在第二次作业中是没有这个bug的,但这次对第一条请求有了更特殊的要求,要求只能(FR,1,UP,0),我只是用字符串是否相同的方式判断了一下,没能够考虑到特殊中的普遍性。
  2. 一次开门完成了多条请求,没能按请求发出时间顺序输出。
  3. 没能实现“一个请求完成后,其附带的顺路捎带请求可能未完成,此时按照时间顺序,将未完成的最先顺路捎带请求升级为主请求”。由于我没能发现不按“时间顺序”与“按时间顺序”有什么区别,经过多次思考后没有想清楚,主观臆测,存在侥幸心理,就导致了这个bug的产生。

  上面的3点都是给我互测的同学发现的,这里要感谢这位同学。

  最后再分析一下第2、3个bug与设计结构的关系。这两个bug都位于ALSScheduler类中,具体在多个方法中都有体现,究其原因,是使用了Java标准类库中的优先队列。这个队列专用于捎带队列,我按照到达楼层的时间(先后)作为各个请求的优先级,能够最早达到的,排在队首,晚到的,排在后面。问题出在同一时刻进队的请求,在出队时可能失去了输入时的顺序以及请求发出的时间顺序,这就导致了第2个bug的产生。另一方面,当主请求执行结束时,处在捎带队列队首的请求未必是按照请求发出时间最早的。

  我能想到的解决办法是专门实现捎带请求队列类,兼顾到达时间顺序与请求时间顺序。在下一次作业中我会尝试着改正。

 

转载于:https://www.cnblogs.com/eggert/p/8688046.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369155.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

想提升java知识的同学请进

这是我最近在整理的笔记,大家可以看看。 https://www.gitbook.com/book/jackal007/java_more/ 如果觉得有问题可以提出,随时修改。 这个笔记是我花了好多时间看各种书籍,博客后整理的,希望能帮助到更多的朋友。转载于:https://www…

mysql 3种报错_MySQL读取Binlog日志常见的3种错误-阿里云开发者社区

MySQL读取Binlog日志常见的3种错误mysqlbinlog: [ERROR] unknown variable default-character-setutf8mb4当我们在my.cnf中添加default-character-setutf8mb4选项,那么在mysqlbinlog查看binlog时就会报错。解决方案:.mysqlbinlog 后面添加 --no-defaults…

膨胀的JavaBeans –不要在您的API中添加“ Getters”

我已经最近在博客的想法的JavaBeans™如何可以扩展以减少在Java世界中,这被广泛接受的公约设立的膨胀。 该文章在DZone上重新发布,并在这里获得了颇具争议的反馈(例如,大多数试图将一些新想法带入Java世界的想法)。 我…

css:hover伪类的使用

&#xff1a;hover的使用&#xff0c;即当鼠标指针移入元素时&#xff0c;所做出的样式设置 示例一 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>demo01</title><style>*{margin: 0;p…

Redis内存淘汰机制

转自&#xff1a;https://my.oschina.net/andylucc/blog/741965 摘要 Redis是一款优秀的、开源的内存数据库&#xff0c;我在阅读Redis源码实现的过程中&#xff0c;时时刻刻能感受到Redis作者为更好地使用内存而费尽各种心思&#xff0c;例如最明显的是对于同一种数据结构在不…

python字符串常见操作

字符串常见操作 如有字符串mystr hello world itcast and itcastcpp&#xff0c;以下是常见的操作 <1>find 检测 str 是否包含在 mystr中&#xff0c;如果是返回开始的索引值&#xff0c;否则返回-1 mystr.find(str, start0, endlen(mystr))<2>index 跟find()方法…

mysql数据库的体系结构包括什么组件_MySQL数据库的体系结构

MySQL体系结构遵循C/S模型&#xff0c;即客户机/服务器模型&#xff0c;MySQL客户机通过原生的多种接口&#xff0c;可以与MySQL服务器进行交互。MySQL数据库是单进程多线程的架构&#xff0c;同windows平台下的Oracle数据库与全平台下的达梦数据库一样&#xff0c;采取单进程多…

同步多线程集成测试

测试线程非常困难&#xff0c;这使得为要测试的多线程系统编写良好的集成测试非常困难。 这是因为在JUnit中&#xff0c;测试代码&#xff0c;被测对象和任何线程之间没有内置的同步。 这意味着&#xff0c;当您必须为创建并运行线程的方法编写测试时&#xff0c;通常会出现问题…

调整图像的灰度级数C++实现

图像灰度级数我们见得最多的就是256了&#xff0c;如果想调整它的灰度级数&#xff0c;我们可以使用图像库的imadjust函数来作出调整&#xff0c;比如讲256个灰度级变成2个灰度级&#xff08;也就是二值图了&#xff09;。再举一个例子&#xff0c;原来一幅256个灰度级的图像&a…

angular4获得焦点事件_Angular 4 文本框自动获取焦点二

Angular是不推荐直接通过DOM操作获取元素的&#xff0c;要操作元素就通过ViewChild装饰器。在HTML中对元素添加引用myInput&#xff1a;在ts中可以通过ViewChild获取指定元素的引用&#xff1a;import { ViewChild } from angular/core;ViewChild(myInput) input;获取到对应元素…

Bootstrap源码解读之栅格化篇

本文纯属自己研究所写笔记&#xff0c;如果有错误还请多多指教提出 版心(container) 版心&#xff1a;class名为.container的容器&#xff0c;其版心的宽度在各个屏幕设备下是不一样的值&#xff0c;版心两边就是留白。 各尺寸下版心宽度如下表&#xff1a; 屏幕设备版心宽度ma…

EasyCriteria 2.0 – JPA标准应该很容易

在今天的帖子中&#xff0c;我们将看到名为EasyCriteria的框架的新版本。 在这篇文章的结尾&#xff0c;我们将在博客中看到这里的内容。 不幸的是&#xff0c;JPA标准存在一个巨大的问题&#xff0c;即冗长。 为什么不变得更简单&#xff1f; 像这样认为EasyCriteria框架已经诞…

[BZOJ2095][Poi2010]Bridges 最大流(混合图欧拉回路)

2095: [Poi2010]Bridges Time Limit: 10 Sec Memory Limit: 259 MBDescription YYD为了减肥&#xff0c;他来到了瘦海&#xff0c;这是一个巨大的海&#xff0c;海中有n个小岛&#xff0c;小岛之间有m座桥连接&#xff0c;两个小岛之间不会有两座桥&#xff0c;并且从一个小岛…

excel和python建模_利用Excel学习Python:准备篇

写在前面这个系列我们要利用Excel的知识&#xff0c;学会用python进行数据分析&#xff0c;如果你精通Excel想要用python提高数据分析效率&#xff0c;那么这个系列你来对了&#xff0c;如果你已经是python大神&#xff0c;想要建模/算法等高级技巧的&#xff0c;这个系列可能不…

故障公告:IIS应用程序池停止工作造成博客站点无法访问

非常抱歉&#xff0c;今天凌晨博客站点负载均衡中所有3台服务器的IIS应用程序池突然停止工作&#xff0c;造成 1:20-7:45 左右博客站点无法正常访问&#xff0c;由此给您带来很大的麻烦&#xff0c;请您谅解。 服务器操作系统是 Windows Server 2016&#xff0c;对应的 IIS 错误…

前端HTML以及HTML5(基本标签)

前面一章介绍了一下前端的发展&#xff0c;这章简单介绍一下html的发展以及基本的标签。 一、HTML的发展史 1、概念 超文本标记语言&#xff08;HyperText Markup Language&#xff0c;简称HTML&#xff09;是为 [ 网页创建和其他可在浏览器中看到的信息 ] 设计的一种标记语言…

方法内联在JVM中有多积极?

IntelliJ IDEA中使用Ctrl Alt M 提取方法 。 Ctrl Alt M。 这就像选择一段代码并按此组合一样简单。 Eclipse也有它 。 我讨厌冗长的方法。 对于我来说&#xff0c;闻起来太久了&#xff1a; public void processOnEndOfDay(Contract c) {if (DateUtils.addDays(c.getCrea…

Python正则表达式基础

1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具&#xff0c;拥有自己独特的语法以及一个独立的处理引擎&#xff0c;效率上可能不如str自带的方法&#xff0c;但功能十分强大。得益于这一点&#xff0c;在提供了正则表…

hexdump mysql_linux下mysql数据库定时备份

备份操作命令&#xff1a;mysqldump -uroot --default-character-setutf8 --hex-blob -p123456 test_oa > /usr/software/data_backup/mysql_backup/test.sql以下是完整脚本&#xff0c;加--default-character-setutf8 --hex-blob 防乱码发生。#!/bin/bashdatabasestestus…

新建一个页面

今天我刚好要做一个单页面来展示某些东西。 就一起来看看吧&#xff0c;初学者写的不好请自闭双眼。 先上代码吧&#xff0c;大家看看有什么需要修改的地方。 1 <!DOCTYPE html>2 <html lang"en">3 4 <head>5 <meta charset"UTF-8&q…