软件工程结对编程作业总结
11061160 顾泽鹏
11061151 庞梦劼
一、关于结对编程
这次的软工任务既不是单打独斗的个人任务,也不是集思广益的团队项目,而是人数为两人的结对编程。两个人合作的安排虽并非是第一次,但提出“结对编程”这个概念却是第一回。
《移山之道》中对结对编程有这样的描述“在结对编程模式下,一对程序员肩并肩地、平等地、互补地进行开发工作。两个程序员并排坐在一台电脑前,面对同一个显示器,使用同一个键盘,同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起单元测试,一起集成测试,一起写文档等。”这和传统的两人一组写代码是有着明显的区别的。
在我们完成任务的过程中,也逐渐体会到结对编程所带来的效率。一个人使用键盘编写代码,另一个人在一旁能够及时的发现和改正错误,这样下来能够发现不少一个人工作时发现不了的低级错误。而且,两个人结对编程时,可以随时进行讨论,当有概念模糊或是思路不清晰的时候,可以迅速寻求同伴的帮助,两个人慢慢理清思路,理清逻辑,这样的效率不是一个人工作时可以相比的。而且,两个人时还可以相互督促,能够更加全身心的投入,而且互相都能够学到很多东西。总之,结对编程使我俩都受益匪浅。
当然,结对编程并不是万金油,在哪个项目中都有效。比如,两个人个性不合,或是火气都比较大,很容易因为一点意见不统一而大吵,反而影响了结对编程的效率;亦或是两人水平相差较大,老手在新手面前不耐烦,新手在老手面前紧张拘谨,也会使结对编程事倍功半。
不过笔者在结对编程过程中相处很融洽,并且互相学习到了很多。
顾泽鹏同学的优点:编程能力强,与他人相处融洽,思维活跃;缺点:话不多
庞梦劼同学的优点:机智,活泼开朗,观察力强;缺点:手速慢
二、关于一些好的设计方法
- 信息隐藏(Information Hiding)
信息隐藏指在设计和确定模块时,使得一个模块内包含的特定信息(过程或数据),对于不需要这些信息的其他模块来说,是不可访问的。
其实现在的面向对象程序设计当中的封装性便是信息隐藏的一个很好的例子。程序设计隐藏了某一方法的具体执行步骤,取而代之的是通过消息传递机制传送消息给它。而在我们的程序设计中,信息隐藏也十分重要。例如,在“学生乘电梯”这个事件中,学生只需要知道“往上走按上,往下走按下”即可,并不用关心电梯是如何运行、如何调度的。相反,若是学生(乘客)知道了电梯的调度方法,并不能保证没有人对这个调度做手脚,使得电梯的运行出现故障。因此信息隐藏是十分有必要的。我们在程序设计中也大量使用了信息隐藏的方法,其中最主要也是最直接的就是用private关键字来实现类的封装性,从而将信息隐藏。
2. 接口设计(Interface Design)
众所周知,接口是提供给其他模块或者系统使用的一种约定或者规范。因此接口必须要保证足够的稳定性和易用性。接口必须相对稳定,否则将导致接口的使用者和提供者为了适应新接口而不断修改接口的实现,可能重复进行无用功,严重时影响整个软件开发进度。而且接口是提供给第三方使用的,较难用的接口会导致接口使用者的抱怨。当然,除此之外,接口设计还有许多其他要求,例如:规范性、可移植性、鲁棒性、安全性、兼容性等。我们采用OOD思想来进行接口的设计,比如IRequest、IEvaluator、IPassenger、IScheduler等接口。事实证明,在使用过程中这些接口十分方便。
3. 松耦合(Loose Coupling)
耦合性是指组件(函数)之间相互依赖的程度,而松耦合是指功能函数之间,尽量依赖程度不要太高。否则,修改完一个底层函数后,会对多个上层函数,进行大量的测试。
松耦合的方法,一般是底层函数功能尽量单一,尽量避免修改底层函数。功能相近的函数,可以设计2个以上,不要为了减少代码量,把一个函数的功能设计的太多。
4. 契约式设计(Design by Contract)
契约式设计要求软件设计者为软件组件定义正式的,精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。这种方法的名字里用到的“契约”,或者说“契约”是一种比喻,因为它和商业契约的情况有点类似。契约式设计的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。
契约式设计的提出,主要基于软件可靠性方面的考虑。可靠性包括正确性和健壮性,正确性指软件按照需求规格执行的能力,健壮性指软件对需求规格中未声明状况的处理能力。健壮性主要与异常处理机制相关 。正确性一方面包括对象元素内部运行的正确性,另一个重要方面是与其它对象元素交互时的正确性。契约式设计就是要求提供一套机制,在客户程序与提供者之间明确的声明双方的职责与权利,即契约,并能够对这些契约进行验证。
三、关于单元测试
初次单元测试的结果如下,不甚理想。
后续测试还在进行中,会及时更新。
四、关于UML类图
这里只展示uml类图的一部分
五、关于算法
我们对原有的算法进行了进一步的改进,使之更有效率,更符合生活实际。我们的算法关键是:
对于普通场景下,我们为搭乘电梯的乘客设置了两个数组,分别记录他们从哪一层来以及想要进入哪一层。设置了一个currentfloor变量用来记录电梯当前的停止的位置。如果电梯是上行的,那么下一次要停靠的位置就是在该电梯上面的,按下了想要上楼按钮的乘客或者电梯内乘客中距离当前位置最近的下一个停靠点(选距离最小的一个);下楼同理;
如果是在高峰时刻(比如上班的高峰期),我们统计了进入第0,1层的乘客占总的请求的乘客的比例,如果大于等于0.8,程序就认为当前是上班高峰,那么我会优先安排电梯从最底层开始往上。下班高峰同理
六、感想与收获
经过这几天的努力,终于把这个电梯调度程序完成了。虽然还有很大的改进空间(添加UI、算法优化),但这是目前我们能够达到的最好水平了。在这期间,我们结对编程的两个人互相帮助、互相支持、互相学习,在实践*同进步。不仅体验了结对编程的效率,同时也加深了对C#、面向对象等的理解。可以说这个任务使我们二人受益匪浅。
七、参考资料
如何设计接口? http://blog.csdn.net/zengwh/article/details/4061711
现代软件工程讲义 3 结对编程和两人合作 http://www.cnblogs.com/xinz/archive/2011/08/07/2130332.html
Design By Contract 契约式设计 http://www.cnblogs.com/riccc/archive/2007/11/28/design-by-contract-dbc.html
附加题1:改进电梯调度的interface 设计, 让它更好地反映现实, 更能让学生练习算法, 更好地实现信息隐藏和信息共享。目前的设计有什么缺点, 你会如何改进它? :
在我看来,这个电梯调度的interface设计的还是很不错的,让我有了更深的理解。
其中,我也发现了一些小问题,改进它们能够更好地反映现实:
在现实生活中,虽然电梯里面会写限制人数(比如10人),但是这其实是告诉乘客该电梯的限制重量(所以往往限制人数后面会加上限制重量)。我觉得限制重量才是电梯运行的核心。作为电梯,它本身并不可能获知电梯里面有多少个人,它只能获悉当前的乘客的总重量是否超出了安全线。
所以说,现实生活中,只有超重才会警报,并不会说超过了多少人数产生警报。
附加题2:目前的这个测试程序只有命令行界面, 请给它设计UI界面, 显示乘客/电梯的运动, 并实现之。
附加题3:阅读有关 MVC 和 MVVM 设计模式的文章。
定义:MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用于组织代码用一种业务逻辑和数据显示分离的方法,这个方法的假设前提是如果业务逻辑被聚集到一个部件里面,而且界面和用户围绕数据的交互能被改进和个性化定制而不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式。
附加题4:我们现在的题目是假设电梯到达所有楼层。在现实生活中,多部电梯到达的楼层都不一样,如果是这样
(例如3号电梯到达10-20层,4号电梯到达5-10层)。整个程序框架和你的电梯调度模块应该怎么改变?
**:其实我觉得这个限制应该重点体现在硬件方面而不是调度算法中:
eg.如果一个乘客A在第15层按下了电梯3的请求,那么按照这个条件,电梯应该不响应,但是乘客并不知晓,
可能会一直等待
如果一个乘客B在第1层进入了4号电梯,他按下了6层的按钮,应该不响应(也就是当做没有这个人),
这样的话乘客就会被忽略了,造成很多困扰,浪费了许多时间
如果在硬件方面,比如说电梯内没有设置5-10层的按钮,那么乘客在进入时就会知道该电梯到达不了,从而
立即从电梯中出来。同理,3号电梯在10-20层就不设置请求按钮,乘客自然不会白白浪费时间等待 整个程序框架:首先要对elevators进行限制:
可以添加一个bool函数来进行响应处理:
如果乘客在电梯外请求,先调用这个函数判断该楼层该电梯是否停,如果不停,则不进行响应。
如果乘客在电梯内请求,同样调用这个函数判断,如果不能停,那么不进行请求响应。