面向对象设计与构造第二次总结做业

写在前面

  经过这三次做业的训练,初步掌握了多线程的设计方法。这三次做业让我对这门课有了更深入的认识,这门课的确是一门重课,不只是大难度、高强度,还有它的持久性,吴际老师称其为“昆仑课程”绝不为过。这是身体与心理的双重砺炼。安全

第七次做业——模拟出租车的乘客呼叫与应答系统

---做业内容

  本次做业模拟出租车的乘客呼叫与应答系统,训练线程安全设计方法,同时应用面向对象分析方法和设计原则来开展分析和设计。多线程

  做业涉及的对象主要是地图、出租车和出租车调度系统。乘客的请求从控制台输入,乘客发出请求后,系统向以乘客为中心的4x4区域内的出租车广播,收到广播的出租车进行抢单,广播有必定的时间窗口,窗口关闭后,系统从全部抢单的出租车中,根据出租车信用和出租车与乘客的距离来选择一辆合适的出租车。出租车服务结束后,须要把这次服务的路径、时间等信息输出到文件中。性能

---做业设计

  下面这张图片是此次做业的类图。主要的类有出租车Taxi类,调度Scheduler类和地图RoadMap类。从线程的角度看,共有100个出租车线程,因请求而产生的调度线程,主调度线程,输入处理线程和主线程。输入处理线程负责接收控制台输入并处理,主调度线程对每一请求建立一个调度线程,调度线程在4x4区域广播而后选择一辆出租车,出租车服务结束后,调度线程输出信息,而后结束。线程之间的关系能够从后面的协做图中看到。测试

  RoadMap能够存储地图,找出两点间的最短路径,判断由某点向某个方向是否有路。路径是一个Path对象,由方向+距离表示。做业中每条路的长度都相同,因此就省略了距离信息,只记录方向。Path对象是不可变的,所以是线程安全的。整个程序里只有一个RoadMap对象,但把它设计为不可变对象,所以也是线程安全的,不一样的出租车能够同时调用shortestPath()方法。优化

  因为要输出出租车的轨迹,出租车某一时刻的状态,为了管理这些信息,分别用PathRecord对象和TaxiInfo对象来记录。PathRecord对象是可变的,能够添加新的轨迹。TaxiInfo对象是不可变的。此外,不可变对象还有Request对象,Point对象,这些不可变对象都是线程安全的。编码

  出租车类有serve(), wander(), rest(), takeOrder()方法,分别对应出租车的服务,等待,中止,接单状态。出租车线程主要与调度线程进行交互,所以,将线程协同与同步控制的重点放在出租车线程和调度线程的设计上。线程

 

 

 

---度量分析

  圈复杂度最大值是13,主要来自于RoadMap的构造方法和出租车类的run()方法。NestedBlockDepth最大值是4,主要是两层循环加上if-else嵌套致使的。总共有三处三层嵌套的代码和三处两层嵌套的代码。设计

  代码总行数约是860行,其中Taxi类,Scheduler类和RoadMap类各占约200行。方法代码总行数约570行,每一个方法代码行数最大值是30,平均值是6.2,方法总数是92个。指针

 ---优缺点

  此次做业的bug是忽略了对出发地和目的地相同的请求的处理,以及出租车接单时正好在乘客的位置上的状况。这两种状况都会致使在寻找最短路径构造Path对象时出现空指针。我认为致使问题出现的缘由有以下:rest

  1. 做业指导书明确了出发地与目的地相同算做无效请求,但是在程序写的过程当中忘了这一点,写完了以后没有再看指导书,也没有测试到。
  2. 对于每一个请求,出租车有两段路程,一是从出租车接单时位置到达乘客位置,二是从乘客位置到达目的地。指导书的要求保证了第二点不会出问题,而第一点是没有保证的,因此须要在编码时考虑到。
  3. 构造Path对象时应该考虑到路径为空的状况,但设计和实现的时候都没有发现这个问题。

  我认为此次写的做业优势以下:

  1. 类的职责相对比较均衡。
  2. 在线程的协同和同步控制上基本知足了需求。

  缺点以下:

  1. 一些设计的性能上有提高空间。例如寻找乘客周围4x4区域的出租车,我使用的是对100个出租车都查询一下的方法,其实能够作一些优化。
  2. 复杂度有些高。例如前面提到的出租车线程的run()方法,尽管没有在run()方法里面展开细节,可仍是有多层循环或分支嵌套的状况。在写的时候没什么感受,如今再次回顾时一会儿难以判断出当时为何这么写,这么写对不对。

  每一个方法代码量少,但方法数目多,这一点我不知道算优势仍是缺点。从上面截图中能够看到方法的平均代码行数是6.2,但方法数却有92个。对于阅读代码者来讲,过多的方法数致使难以掌控总体结构,单个方法太庞大致使难以理解局部。若是您愿意同我分享您的看法,请在下面评论。

第六次做业——监控文件属性变化

---做业内容

  做业内容是实现一个监控程序,针对给定监控范围内的监控对象,以扫描方式探查监控对象相关属性的变化,从而触发规定的处理动做。监控范围指计算机文件系统中的一棵目录树,监控对象则是位于监控范围内的具体文件。处理动做包括恢复,记录详细信息,记录概要。做业的目标是训练针对线程安全问题,如何平衡线程访问控制和共享对象之间的矛盾。

---做业设计

  从线程的角度上说,一条监控命令对应一个监控线程,和一个文件扫描线程。Monitor类能判断监控对象的触发器(包括重命名,移动,大小改变,最后修改时间改变)是否触发,并执行相应处理动做。Snapshot类是文件属性的快照,它的结构就是一颗目录树。SnapshotManager是快照管理类,FileScanner是文件扫描线程,它建立快照并把快照交给SnapshotManager管理。Analyzer类和TaskPerformer类分别是快照分析类和任务执行类,监控线程(Monitor)从SnapshotManager中取出最近的快照,并交给Analyzer分析,若是触发,则让TaskPerformer执行处理动做。Summary和Detail类是管理详细信息和概要的类,这些信息会被定时写到文件中去。

  Snapshot对象是不可变的,它记录有扫描时刻目录下全部文件的属性,是一个递归的结构,所以在分析快照时使用起来比较方便。TaskTuple对象也是不可变的,记录有一条监控命令对应的监控对象,触发器及任务。Summary类、Detail类、SnapshotManager类都被设计为线程安全的类,由于全部的监控线程都共享同一个Summary和Detail对象,可能同时被多个线程访问,SnapshotManager在文件扫描线程和监控线程间共享,也须要是线程安全的。SafeFile是线程安全的文件访问类。

 

  

 

---度量分析

  圈复杂度最大值是12,来自对输入处理的方法。经过对过去几回做业的度量分析,我发现输入处理的圈复杂度较高,多是由于须要对多种输入错误的状况作处理,若是您有什么下降复杂度的方法,欢迎在下方评论。

  嵌套深度最大值是5,共有1个4层嵌套和3个3层嵌套的代码(方法内部)。嵌套深度最大值来自Monitor的run()方法,循环和try-catch就占了两层,再加上if-else分支又占了两层,总共就是4层。其余几个3层嵌套都上是一层循环加上两层if-else。这种多层嵌套的代码可读性不强,正确性也难以判断,因此要尽可能避免。

  全部属性的个数是50,方法数是69,总代码量约670行。每一个方法代码行数,平均值是6.4行,大部分在10-20行,有两个方法是30来行。

 ---优缺点

  此次做业的bug是,监控重命名时,若同一目录下有多个大小相同,最后修改时间相同的文件,就会判断出错。判断重命名的标准是原来的文件消失(绝对路径找不到了),新增了一个文件,这个文件的大小和最后修改时间与消失的文件相同。我忽略了“新增”的要求,就出现了这个bug。

  优势以下:

  1. 总体结构比较清晰。
  2. 达到了训练线程安全,掌握如何平衡线程访问控制和共享对象之间的矛盾的目标。

  缺点以下:

  1. 使用了最简单的方法来比较文件名,文件路径,其实能够用散列的方法来提升速度。
  2. 一些地方嵌套深度比较深,不易读,容易出错。

第五次做业——多电梯调度系统

---做业内容

  本次做业是设计一套由3部电梯组成的多电梯调度系统,经过采用线程机制,在第三次做业所实现程序的基础上完成新的调度系统程序。电梯可以支持捎带,调度系统按照运动量均衡策略来调度楼层请求。

---做业设计

  因为此次写的程序类太多,类的关系错综复杂,就不给出类图了。下面这张图是一个大致的结构,每一个方框表示一个对象,每一个椭圆表示一个放请求和取请求的托盘(由于第一次写多线程程序不太熟悉,因此就显式地用“托盘”来表示)。单个箭头表示请求的流动方向,两段都有箭头的,是表示两个线程间有交互。RequestSimulator是请求模拟器,它从控制台中读取输入,产生请求。MultiScheduler是总的调度器,它负责将电梯直接请求分发给各部电梯,而且对楼层请求以捎带和运动量均衡策略进行调度。再往下走是Scheduler,它负责对单步电梯的调度,例如处理捎带请求等等,这就归约到了第三次做业的问题。

---度量分析

  圈复杂度最大值是20,来自Scheduler的调度方法。代码总行数约1200行,方法总数是151个。能够发现后面两次做业(6-7)没有达到这么大的规模,缘由多是电梯调度逻辑的复杂性。

 ---自我评价

  这次做业bug主要有两个。一是对输入的处理上,一行多个空请求的处理与说明文档描述不符,是我疏于测试的问题,若是测试了就可以发现。二是楼层请求的捎带有问题,具体是什么问题就没有深究了,我猜想多是逻辑上的问题加上对线程机制不熟悉的问题。

  缺点以下:

  1. 逻辑混乱。例如调度器的调度方法,电梯的各类方法。一来是继承前面两次做业的设计致使难以修改,牵一发而动全身。二来是加入了多线程后难以梳理出各部分的关系。
  2. 复杂度过高。高的复杂度致使容易出错,难以测试,难以读懂。
  3. 违背了不少设计原则例如OCP、LSP。

  因为距第5次做业的时间比较久远了,代码写得混乱,不忍直视,因此就简略地分析一下。

 结束

  感谢您可以花时间阅读本文。这是我对本身三次做业的总结,大部份内容都是对个人做业的分析,可能读者不太了解具体细节,因此,写做时我尽量避开细节,这样就显得有些宽泛。若是您有疑问或者建议,请在下方评论。

相关文章
相关标签/搜索