结对编程-电梯调度
结对编程-电梯调度
成员:
李孟 11061190 吕佳辉 11061152
我们眼中的结对编程
在实际的软件开法过程中结对合作是一种十分普遍的现象。这就像驾驶汽车或是飞机,需要有一个正驾驶和一个副驾驶一样,结对编程是两个人一起合作进行软件开法和设计的过程。其中需要有一个主要负责人,即正驾驶,他负责在整个开法过程的大方向。另一个人则做一些辅助工作,即副驾驶,他负责协助主要负责人进行工作。所以这一过程需要两个人有明确的分工和团结一致的思想。在历史上,许多比较著名的大的IT公司都是由两的人共同一手创办的。例如,谷歌,苹果等等。这也足以说明两个人的共同合作在做大事方面的利处是无穷的。而造成这一切的最主要的原因是结对编程相比于一个人单独编写程序而言是有许多优点的。
首先,结对编程的效率要比一个人编写要高。这体现在三个方面。一是两人合作可以开拓思路,相互启发,正如俗语所言“三个臭皮匠顶个诸葛亮”,毕竟两个人的智慧要比一个人的强。二是两个合作互有分工,所做的工作量平摊下来要比一个人的所需要做要少。三是两个人合作,就需要不停地碰头,开会,相互检查对方的进度以决定自己下一步的工作,所以这也就在无形中起到了督促检查的作用。
其次,结对编程也是一个非常好的相互学习借鉴的机会。两个人可以在共事中,学习对方的优点,了解新的知识。这也会在两人之间形成一点竞争的氛围,使二人共同进步。除此之外,在结对的过程中,我们将会学到两个人如何合作。这也是我们人生的一门必修课。因为,在合作的过程中,肯定会有分歧,会有摩擦,这时两个人都要学会妥协,学会退一步就海阔天空的处事方式。
而且,结对编程还有一个相比于个人单独奋战而言的巨大优势,即结对编程的代码出错率会比较低。因为,如果只有一个人独自完成所有工作,他将面临写代码,调试代码,测试代码的所有工作。这必将对于代码的正确性产生巨大的负面影响。因为自己调试和测试自己所写的程序有时容易出现一根筋的问题。但是两个人一起工作,就可以相互调试测试对方的程序,这样可以使出错的几率降低。
当然,结对编程也不是十全十美的。这种工作模式的最大缺点就是两个人能力的差别将会对团队的整体实力造成比较大的影响。团队中实力相对较弱的一个人将会成为团队中的短板。所以,在结对编程时应该相互帮助,尽量克服这一弊端。
而且,结对编程相比于一个人单独编程还需要掌握更多的软件编写技术和一些双方都要遵循的标准。因为如果两个人编写代码时各自行事,将会给后续的代码合并为一个完整的工程这一工作造成十分不利的影响。所以,在进行结对编程时,两个人应该进行一次简短的会议,商讨两人在进行编程时应遵守的共同标准,例如变量和方法的命名规则,接口的设计,程序的总体框架,每个人负责设计的类,全局变量的设计等等。如果以上这些问题在两人开始进行工作之前没有沟通和协商好,将会后患无穷。
我们的团队
我们的团队由两个性格和个性迥异的人组成。在这两周的合作时间里,我们共同完成了分配的任务。在这里,我们盘点一下我们各自的优缺点。
首先是李孟同学,他是个精明能干,具有一定协调组织能力,且待人真诚的人。在我们的团队中,他总是能够处于一个决策者的位置上。但是人无完人,每个人的身上都或多或少有一些缺点。李孟同学虽然总是能够出谋划策,但是,他的编程能力还有待提升。
其次,我们的第二位成员就是吕佳辉同学。他是一个肯吃苦耐劳,善于合作,性格随和的人。不过有时办事有些拖沓,这一点还需要改正。
契约式设计
说了这么多,我们都是在泛泛谈结对编程。而结对编程需要用到不少软件工程里的软件开法技术。这些都是前人总结的经验,对于我们今天的工作具有十分重要的指导意义。
契约式设计(Design by Contract)是一种设计计算机软件的方法。这种方法要求软件设计者为软件组件定义正式的,精确的并且可验证的借口,这样为传统的抽象数据类型又增加了先验条件、后验条件和不变式。
这样的做法对于结对编程是十分有利的。因为这样做对于代码的调试和正确性检验都是十分有利的。
UML图
算法关键
对于电梯调度的问题,我们最直接能想到的就是贪心算法。但是贪心算法可以有非常多种,实现的程度可以很不一样。这部分我们将阐述我们的调度算法的思想和具体的实现方式。
我们的调度算法是把电梯作为主角,对请求作出选择和响应。对每个电梯建立一个“任务列表”,向其中放入该电梯即将执行的一系列任务。每个任务就是一个目标楼层,按照从小到大(如果电梯运行方向向上)或者从大到小(如果电梯运行方向向下)排序。电梯一次执行任务列表里的第一个任务。在world engine不断的刷新中,任务列表根据请求的增加和任务的完成情况更新任务列表。
首先,我们对请求队列进行处理。如果该时刻下的某一个请求来自电梯外部,将它加入到请求队列中;如果该时刻下的某一个请求来自当前电梯内部,将该请求的目标楼层加入到该电梯的任务队列中,并根据运行的方向对任务队列进行排序。
接着,对当前电梯我们进行如下安排:
如果当前电梯停在某个楼层,而且它的任务列表里有任务,此时对任务列表的第一个任务进行判断:如果该任务指示的目标楼层是当前电梯所在楼层,也就是说该电梯完成了这个任务,则从任务列表中把这个任务删除;如果该任务指示的目标楼层不是当前电梯所在楼层。那么执行这个任务。
倘若这个电梯的任务列表为空,也就是说它已经没有任务了,此时该电梯去请求列表中寻找请求发出点离它最近的那个楼层作为新的目标楼层,并执行这个任务。
以此,该算法成功地完成了电梯调度。
单元测试
信息隐藏
信息隐藏(Information Hiding), 就是将所有的模块独特地设计好,使它们的信息(数据和算法)不被其他模块使用(除非设计中需要被使用)。“隐藏”就是说,有效的模块性是可以通过定义一系列独立的模块实现的。这些模块相互只交流对整个软件功能有用的必要的信息。“抽象”有助于定义组成软件的程序上的实体。“信息隐藏”的这种设计方式十分有助于软件的测试和后期的维护。因为大多数的数据和细节被隐藏在软件的特定部分中,在模块里由于疏忽引起的错误将不容易蔓延软件的其他部分。
我们可以看到,把整个软件分为几个独立的模块,采取信息交流的方式进行沟通,这类似于编写独立的类并设置相应的接口。而从我们的经验里我们确实感受到这样一种封装的方式能够带来很多的好处。对于一处的改变仅在小范围内引起变化,这样更有助于维护。而数据和细节的隐藏,更有利于安全。确实,有些内部的信息,既然被其他模块知道是不必须的,为什么要让其他模块干预自己呢。
界面设计
界面设计有这样的黄金规则,基本上总结了界面设计所应该遵循的原则。在设计界面时需要参照以下这样的规则去处理,将有助于做出更好的设计。
界面设计的黄金规则 |
|
1.用户操纵控制 |
1.以不强迫用户进入不必要的动作或不希望的动作的方式来定义交互模式 2.提供灵活的交互 3.允许用户交互被中断和撤销 4.当技能级别增长时可以使交互流线化并允许定制交互 5.使用户与内部技术细节隔离开来 6.设计应允许用户与出现在屏幕上的对象直接交互 |
2.减少用户的记忆负担 |
1.减少对短期记忆的要求 2.建立有意义的缺省 3.定义直观的快捷方式 4.界面的视觉布局应该基于真实世界的象征 5.以不断进展的方式揭示信息 |
3.保持界面一致 |
1.允许用户将当前任务放入有意义的环境中 2.在应用系统家族内保持一致性 3.如果过去的交互模型已经建立起了用户期望,除非有不得已的理由,否则不要改变它 |
松耦合
在软件领域,“耦合”一般指软件组件之间的依赖程度。松耦合有助于降低客户端和远程服务之间的依赖性。松耦合系统的优点在于更新一个模块不会引起其它模块的改变,可以降低整体复杂性和依赖性。松耦合使应用程序环境更敏捷,能更快地适应更改,并且降低了风险。送耦合实体独立,易于维护。
松耦合提供了一种较为宽松的联系,使得组件相互的依赖性不强,而各自的独立性增强。我们在编写软件时也应当尽量让组件独立起来。
附加题
1、阅读有关 MVC 和 MVVM 设计模式的文章。说明你写的电梯调度的UI /Algorithm/interface 如何实现了MVC 或MVVM 的算法。
在阅读了MVC和MVVM的相关文章和百度介绍后,我们了解到:
MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式。而MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。
MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点
1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。
4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
在我们的调度算法里主要实现了MVC模式,即将输入输出和处理分开。调度主算法就是负责处理部分,处理电梯的运行和调度。
2、我们现在的题目是假设所有电梯到达所有的楼层。 在现实生活中, 多部电梯到达的楼层都不一样。如果是这样 (例如3号电梯能到达10 – 20 层, 4 号电梯能到达5-15 层),整个程序框架和你的电梯调度模块要做什么改变? 请说明你的改进意见
现实生活中,我们发现电梯对于此类情况的处理方法是:在电梯内部只能按下可以到达的楼层的按钮;对于不到达楼层的按钮,即使乘客按下,按钮的灯不会亮,电梯不会响应这个请求。于是这个乘客就要考虑从某一层先出去,再采取其他措施。
而在电梯外部,对于不到达该楼层的电梯,它不会响应乘客的请求。对该电梯来说,此请求不存在。
我们可以针对以上两点对整个框架和算法做些改变。
(1)对于外部请求的处理,主要在调度算法里进行修改。在原算法里,电梯会考虑所有请求。但是现在的情况下,电梯要检测请求的来源楼层。如果电梯发现某个请求来自于它所不到达的楼层,忽略这个请求。
(2)对于内部请求,需要对框架做些修改。电梯在乘客进入之前不会知道该乘客要去哪个楼层。而乘客在发现自己原先要去的楼层无法到达时,他会改变自己的选择,也就是重新选择自己离开电梯的楼层。那么,我们要在框架里,需要在乘客类里加上决策方法。当该乘客发现自己的目标楼层请求不被满足时,改变他的目标楼层。
这样,我们就可以通过这个程序描述整个电梯调度问题。