结对项目:电梯调度算法的实现和测试
12061205 王力民 12061196 金鑫
这就是我们两个忙着编代码的样子啦,之前也没有尝试过这样一种结对编程的方式,第一次这样合作,还是有不少感想与收获的吧。
一、关于结对编程的优缺点
优点:
1. 在开发层次,结对编程能提供更好的设计质量和代码质量,两人合作能有更强的解决问题的能力。
2. 对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感,同时,能够降低学习成本,一边编程,一边共享知识和经验,有效地在实践中进行学习。
3. 在心理上, 当有另一个人在你身边和你紧密配合, 做同样一件事情的时候, 你不好意思开小差, 也不好意思糊弄。
4. 在企业管理层次上,结对能更有效地交流,相互学习和传递经验,能更好地处理人员流动。因为一个人的知识已经被其他人共享。
缺点:
1.对于有不同习惯的编程人员,可以在一起工作会产生麻烦,甚至矛盾。
2.程序员们有时会对一个问题各执己见,导致浪费很多时间彼此争论。
3.两个人在一起工作可能会出现工作精力不能集中的情况。程序员可能会交谈一些与工作无关的事情,反而分散注意力,导致效率比单人更为低下。
4.有经验的人更喜欢单兵作战,找个人来站在他背后看着他可能会让他感到非常的不爽,最终导致编程时受到情绪影响,反而出现反作用。
二、结对成员
- 王力民
编程能力强,思维清晰,有耐心。有时想的太多。主要负责编码。
- 金鑫
愿意花时间,细致,认真。代码编写能力不足。主要负责博客撰写。
三、关于设计方法
- Information Hiding,信息隐藏。
将程序模块化时,保证每个模块对其他所有模块都隐蔽自己的设计决策,让模块规定并设计成为在模块中包含的信息不被不需要这些信息的其他模块访问。应用有在多层设计中的层与层之间加入接口层,使所有类与类之间都通过接口类访问和 类的所有数据成员都是private,所有访问都是通过访问函数实现等。
- Interface Design,接口设计。
软件的接口设计元素描述了信息如何流入和流出系统以及被定义为体系结构一份的构件之间是如何通信的。这次的作业中用到的主要是各种设计构件之间的内部接口,通过这些接口让软件体系结构中的构件之间进行内部通信与协作。接口实现时,保证所有操作和消息传递模式都得到实现,且不同类的操作之间能够进行通信和协作。
- Loose Coupling,松耦合。
耦合是类之间彼此联系程度的一种定性度量。因为随着通信和协作数量的增长,类之间的联系程度越来越强,系统的复杂性也随之增长了,类之间的耦合程度也就增加了。通过增加接口,使类之间的通信协作通过接口很好的完成,保证程序尽可能保持低耦合。松耦合系统通常是基于消息的系统,此时客户端和远程服务并不知道对方是如何实现的。客户端和服务之间的通讯由消息的架构支配。只要消息符合协商的架构,则客户端或服务的实现就可以根据需要进行更改,而不必担心会破坏对方。松耦合通讯机制提供了紧耦合机制所没有的许多优点,并且它们有助于降低客户端和远程服务之间的依赖性。
四、Design by Contract, Code Contract
- Design by Contract,契约式设计。
优点:
(1) 这种方法要求软件设计者为软件组件定义正式的,精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。因此,契约式设计中的函数,期望所有调用它的客户模块都保证一定的进入条件,这样它就不用去处理不满足先验条件的情况。同时,保证了退出时给出特定的属性。做到在进入时假定,并在退出时保持一些特定的属性。
(2) 使调试过程更简单。便于程序中的错误的查找与解决。
(3) 能帮助代码重用,因为每段代码的契约都被很好的文档化了,模块的契约可以被当做软件文档来描述模块的行为。
缺点:断言不能沿着继承层次往下遗传。如果你重新定义了某个具有合约的基类方法,实现该合约的的断言不会被正确调用,你必须手工调类不变式,基本的合约不会主动实现。
使用:
- Code Contract
优点:
(1) 规范了协定,统一了代码。显式区分了前置条件和后置条件,并将断言也统一了起来。
(2) 改进测试。代码协定提供静态协定验证、运行时检查和文档生成。
(3) 自动测试工具。可以使用代码协定来筛选掉不满足前置条件的没有意义的测试参数,从而生成更有意义的单元测试。
(4) 静态验证。静态检查器可以在不运行程序的情况下确定是否存在任何违反协定的情况。它会检查隐式协定和显式协定。
(5) 引用文档。文档生成器在现有的 XML 文档文件增加协定信息。还提供了可与 Sandcastle 一起使用的样式表,以便生成的文档页具有协定节。
五、UML
各个实体之间的关系如上类图所示。
六、算法
1、关于算法这一块,我们最开始讨论出了一个比较完善的算法。
以每位乘客为单位建立请求队列,每个请求包括乘客所在的楼层和要去的楼层。
然后以以下条件判断电梯是否开门:
1)电梯经过每一层的时候都判断是否有请求等候在这一层,如果有并且顺路,就满足请求。(这里加一个优化,就是计算一下电梯停下来所花费的时间和其他楼层的电梯到这里满足指令所花费的时间,选择较小的)
2)如果有空电梯,立即满足请求。
后来在实现的时候,发现老师给的框架是一个乘客发出请求时建立一个请求对象,进入电梯后再建立另一种类型的请求对象,以此构成请求队列。与预先设计的算法有些出入,实现起来略有难度。
2、后来在分析过源代码之后,我们设计了另一个算法。
1)源代码是在每一层都停下来,乘客上下电梯都是自动完成的。后来助教给了一个优化的版本,是寻找下一个需要停的目标楼层,这样做是考虑了disablefloor。
2)因此我在想是否可以在找下一个目标楼层的时候加入优化,即各种限制条件。
3)但编码实施一段时间后我发现自己想错了。那个NextAvailableFloor方法并不是用来找下一个目标楼层,而是下一个可以停的楼层。换句话说也就是靠这个方法依次返回每个楼层来驱动电梯运行,如果在这个方法中加入优化,可能导致电梯长时间无法运动。
3、最后跟宿舍的另一个队的队员讨论之后,得出了一个还算可行的算法。
1)只修改Scheduler中的调度算法。
2)以电梯为驱动,即在时间的循环内每一个时刻都遍历四部电梯,刷新电梯运行状态。
3)对每一部电梯,都遍历请求队列。
a)分电梯外和电梯内请求考虑
b)要带走顺路的电梯外请求
4)遍历完请求之后改变电梯的运行状态
七、总结
1、经过这次之后,我深深地体会到了对项目做好规划的重要性。最开始我就是随便看看代码想想算法,但其实在实现的时候才发现很多细节都没有注意到。于是重新根据程序运行顺序读了一遍代码,思考了代码模版的框架,最终完成了这次项目。
2、要深刻地理解面向对象编程中各个对象各司其职的思想。最开始我希望去修改elevator类中的NextAvailableFloor来实现优化。但其实犯了一个严重的错误,本应就调度算法进行优化,我却希望修改elevator中的方法。最后想明白这一点之后,修改了Scheduler中的调度算法。
3、由于个人编程能力有限,在修改Scheduler中的调度算法时,之前想过的很多优化都没有实现进去,导致最后程序效率并不高。关于这一点,只能通过今后的锻炼来提升自己,增强编程能力。