(1)从多线程的协同和同步控制方面,分析和总结自己三次作业来的设计策略及其变化。

第5次作业:多线程电梯

基本照搬了课件上“生产者-消费者”模型的设计策略,将InputHandler设计为生产者线程,将Scheduler设计为消费者线程,将RequestQueue设计为托盘。生产者与消费者的工作并发,提高效率。同时,每部电梯设计为一个线程,因为每部电梯的运行彼此不干扰。InputHandler, Scheduler由主线程创建,三部电梯由Scheduler负责创建,这样使得调度器可以获取电梯的状态。但这种策略并不是最优的。

第6次作业:文件监控

这大概是写得最失败的一次作业。

按课件方法构建了线程安全的SafeFile类。实现了同步化的访问文件状态、写文件方法。然而对于每个record型请求单独建一个文件写入,违背了训练初衷。

每个请求设计一个“监控器”线程,直接查看对应文件的状态,与指导书的设计思路差异较大。这使得程序难以应对path-changed等大规模变化。

总的来说,这次多线程之间没什么协同可言。同步控制也仅限于对文件状态的访问。

第7次作业:出租车调度

这次作业的设计策略基本照搬第5次作业。输入处理线程为生产者,调度器线程为消费者,请求队列为托盘。每辆出租车为一个线程,依然由调度器创建。

写了线程安全的SafeFile类用于输出文本文件。乘客请求、参与抢单的出租车信息由调度器负责写入,出租车运行情况由出租车线程负责写入,实现了线程协同。

(2)基于度量来分析自己的程序结构

1)OO程序代码度量

第5次作业:

类名 属性个数 方法个数 代码规模 main / run方法规模 点评
Elevate_5 0 1 27 17  
Elevator 14 7 176 40 电梯控制逻辑复杂。
Floors 2 4 24    
InputHandler 10 6 158 15 写得太随意,很多属性更适合作为局部变量。
Request 4   59    
 -CarryRequest 1 3      
 -FloorRequest 1 3      
RequestQueue 6 4 56    
Scheduler 7 8 209 40 保留了上次的调度器代码。
Tray 3 4 76    

第6次作业:

类名 属性个数 方法个数 代码规模 main / run方法规模 点评
Detail 4 3 64    
FileState 4 3 26    
InputHandler 9 6 107   相似的问题
Monitor 8 2 60 30  
SafeFile 2 15 110   为测试者写了较多的方法
Summary 4 3 64    
TestDrive 0 1 15    
TestThread          
Trigger 3 8 94   设计有问题

第7次作业:

类名 属性个数 方法个数 代码规模 main / run方法规模 点评
_Point 2 7 40    
CHandler 9 4 76 32  
CityMap 4 4 114   算法代码比较长
FHandler 2 4 55    
Queue 3 3 49    
Request 3 3 24    
SafeFile 2 5 57    
Schedule 6 6 146 44 调度方法夹杂输出代码
Taxi 16 9 204 40 运行方法夹杂大量输出代码
TestDrive 0 1 18 12  

3)各次作业的类图

第5次作业:由于指导书给出了上次电梯的参考类图,自己的设计基本遵循了指导书。但是Tray这个类没能发挥出应有的作用,只是单纯地缓存请求。更好的做法是同时缓存电梯状态,电梯运行时更新,调度器需要时读取。

第6次作业:写得很糟糕,有“面条代码”的嫌疑。对Monitor和Trigger两个类的职责没有明确,而且Summary和Detail的代码有大量重用。

第7次作业:很多地方参考借鉴了第5次作业(多线程电梯)。各个类的职责相对清晰。

4)UML的协作图(从上到下依次是第5,6,7次作业的图)

5)设计原则检查(仅针对第7次作业)

DIP (Dependency Inversion Principle):高层次不依赖低层次。

程序有两个渠道获取输入,一是通过文本文件获取城市地图,二是通过控制台获取叫车请求。我的程序没有将这两种途径进行抽象和归纳,导致程序对输入方式变化的适应性不好。

命名:

在为类、实例变量命名时我尽可能遵循“顾名思义”,但是走向了另一个极端。我将一个记录点信息的类命名为“Point”,结果后来发现有java.awt.Point这个类,使用GUI时很不方便。无奈之下我将自己写的类改名为“_Point”,花了一部分时间改名字。在顾名思义的同时,也尽量不要取过于简单、大众化的名字,否则容易与JAVA类库重名,造成麻烦。

另外,eclipse建议包名首字母小写,类名首字母大写,作为初学者还是遵守得好。

(3)分析自己程序的bug

第5次作业:

所有的“正确请求测试多线程功能”样例均有错(公测未发现或是被发现bug)。

问题所在的类是Scheduler。

被发现了一个CRASH是由于想向文件写入结果,但文件已经关闭导致的。这暴露出我对于多线程同步控制尚未掌握,且自己构造的测试样例太宽松,很容易露出破绽。

从设计结构角度分析,我把所有的电梯调度方法全写入一个调度器类。Scheduler类的代码规模更是破了200行,远远超过其它类。

在总结课上,我才了解到有更加均衡的实现方式。总调度器只负责将请求分配给不同的电梯,而每部电梯对应一个专门的调度器负责排请求的执行先后顺序。这种方式显然更合适,既是遵循均衡原则,又符合实际的应用场景。

第6次作业:

对方没有进行公测,且没有发现bug,故不作分析。

第7次作业:

被发现了一个bug,为新增节点“等待状态下出租车的运行不具备随机性”。

问题所在的类为Taxi,方法为run_edge

这个bug被发现在情理之中,因为我曾经尝试过实现出租车的随机运行,但是失败了(出租车发生了瞬移)。最终改成了一版确定性运行的方法。

(4)分析自己发现别人程序bug所采用的策略

很多学生(包括我)在编写多线程程序时遇到的一个技术难点就是如何让程序正常结束(在eclipse下是在控制台上方看到<terminated>)。尽管这不是指导书的硬性要求,但是能够正常结束的程序势必带来更好的用户体验和更强的鲁棒性。如果被测者没有在readme中对程序的结束进行说明,或是说明能结束但实际上没实现的,则可以报告bug。

边界条件是在编程过程中需要着重考虑的。一些在实际情况中完全有可能出现,但编程者很难考虑到的测试点上往往容易发现bug。第7次作业我通过构造“乘客请求起始坐标正好是出租车所在位置”的样例,成功发现了别人的bug。

后期的作业不但注重程序正确性,而且对程序设计原则也提出了要求。测试者可以阅读被测代码,发现其中不符合设计要求的部分,予以扣分。

(5)心得体会

1)线程安全

在线程安全方面,重点是把握课上重点讲过的“生产者-消费者”模型,学会将共享对象的存取方法设置为同步化的,以及方法内部具体的写法。拿到指导书之后,在分析程序需要哪些对象、对象之间关系时,必须额外考虑哪些对象需要被共享的问题。无论是自己写被共享类的代码,还是包装为线程安全类,都要想清楚哪些方法需要保证原子性,并使用synchronized关键字。

2)设计原则

从第7讲开始,课后作业将程序设计原则也纳入了作业要求和互评范围。设计原则与程序的正确性没有直接的关系,而是为了让程序具有较好的可读性、可扩展性。在软件开发行业,用户需求发生变更是经常遇到的事件。遵循课上讲过的一些设计原则可以增强程序的可扩展性。在需求变更时,只需要对原有程序进行有限的修改,而不是推倒重来。自己在3次电梯作业中,后面的作业往往不能有效利用前面作业的代码,原因在于自己没有在一开始构造一个良好的设计,没有遵循设计原则。另一些原则是为了程序可读性,因为将来我们开发的程序只是一个大项目的一部分。不但要保证别人放心调用,而且最好在代码中清楚写出自己的逻辑。可能经过后面几次作业的训练,我能够更深入地理解设计原则。

OO博客作业2:第5-7周作业总结的更多相关文章

  1. OO博客总结——OO落下帷幕

    OO博客总结--OO落下帷幕 凡此过往,皆为序章. 不知不觉OO课程即将落下帷幕,一路坎坎坷坷磕磕绊绊,可算是要结束了,心里终于松了一口气,也有小小的不甘和遗憾.凡此过往,皆为序章.特殊的线上OO课程 ...

  2. OO博客作业-《JML之卷》

    OO第三单元小结 一.JML语言理论基础以及应用工具链情况梳理 一句话来说,JML就是用于对JAVA程序设计逻辑的预先约定的一种语言,以便正确严格高效地完成程序以及展开测试,这在不能容忍细微错误的工程 ...

  3. OO博客作业1:第1-3周作业总结

    (1)基于度量来分析自己的程序结构 注:UML图中每个划分了的圆角矩形代表一个类或接口,箭头可代表创建.访问数据等行为.类的图形内部分为3个部分,从上到下依次是类的名称.类包含的实例变量(属性).类实 ...

  4. 第二次oo博客作业--多线程电梯

    这次的系列作业是写一个电梯调度,主要目的是让我们熟悉多线程. 第一次作业是一个傻瓜电梯的调度问题,要求也很简单,即每次接一个人就行了.我只用了两个线程,一个是输入线程,一个是电梯线程,输入线程负责从标 ...

  5. 小菜鸡儿的第三次OO博客

    规格化设计历史 规格化设计的历史目前网上的资料并不多,百度谷歌必应也表示无能为力...... 在这里结合现实情况讲一讲自己对程序规格化的理解,首先代码规格化对代码的影响是间接的,或许它不能让你代码里面 ...

  6. 接着继续(OO博客第四弹)

    .测试与JSF正确性论证 测试和JSF正确性论证是对一个程序进行检验的两种方式.测试是来的最直接的,输入合法的输入给出正确的提示,输入非法的输入给出错误信息反馈,直接就能很容易的了解程序的运行情况.但 ...

  7. OO博客作业3:第9-11周作业总结

    一.总结介绍规格化设计的大致发展历史和为什么得到了人们的重视 1.规格化设计的大致发展历史 规格化设计,又称契约式设计,最早由Bertrand Meyer于1986年提出,出自于<面向对象软件构 ...

  8. OO博客作业4:第13-14周作业总结

    一.论述测试与正确性论证的效果差异,比较其优缺点 测试是设计若干组测试用例,运行程序并检验其是否完成预期功能.测试是一种直接发现BUG的方法,可以准确断定什么样的BUG会发生,并通过辅助调试进一步确定 ...

  9. 第四次oo博客作业

    (1)本单元是撰写UML数据分析器,架构大致如下,在指导书要求的函数外,对于UmlClass类,Umlinterface类,以及状态机,顺序图这四个类重现构造一个类,这个类里有他们所需要的全部信息,另 ...

随机推荐

  1. Java 关键字 速查表

    访问控制:private 私有的protected 受保护的public 公共的 类.方法和变量修饰符abstract 声明抽象class 类extends 扩允,继承final 终极,不可改变的im ...

  2. [20190328]简单探究sql语句相关mutexes.txt

    [20190328]简单探究sql语句相关mutexes.txt --//摘要:http://www.askmaclean.com/archives/understanding-oracle-mute ...

  3. Scrapy(爬虫框架)中,Spider类中parse()方法的工作机制

    parse(self,response):当请求url返回网页没有指定回调函数,默认的Request对象的回调函数,用来处理网页返回的response,和生成的Item或者Request对象 以下分析 ...

  4. python——函数之装饰器

    1 问题 实际生活中,我们很难一次性就把一个函数代码写得完美无缺.当我们需要对以前的函数添加新功能时,我们应该怎么做? 2 问题解决思路 (1)可以直接修改原来的函数,在函数内直接修改.当我们对多个函 ...

  5. February 12th, 2018 Week 7th Monday

    One man's fault is another man's lesson. 前车之覆,后车之鉴. We make mistakes every day, large or small, fail ...

  6. 团队作业——Beta冲刺

    团队作业--Beta冲刺 经过紧张的Alpha阶段,很多组已经从完全不熟悉语言和环境,到现在能够实现初步的功能.下一阶段即将加快编码进度,完成系统功能.强化软件工程的体会.Beta阶段的冲刺时间为期5 ...

  7. 《Java大学教程》—第21章 高级案例研究

    21.3    需求:P510用例模型(use case model):用例图(use case diagram).用例(use case).行为说明(behaviour specification) ...

  8. python数据类型练习题

    一.元素分类 有如下值集合 [11,22,33,44,55,66,77,88,99,90...],将所有大于 66 的值保存至字典的第一个key中,将小于 66 的值保存至第二个key的值中.即: { ...

  9. Teradata 批量查找PI字段

    select * from dbc.indicesv where indextype in ('P','Q');

  10. C#语言のC#扩展方法(.Net特性)

    this在C#中的常见用法:1.在C#中,this关键字代表当前实例,我们可以用this.来调用当前实例的成员方法,变量,属性,字段等; 2.也可以用this来做为参数状当前实例做为参数传入方法. 3 ...