结对项目——电梯调度(final)

结对项目——电梯调度

by 曹非凡

Part0 前言

在一开始看到项目需求时,我们两个其实并没有什么思路,所以去网上搜索了类似的项目,从中获取灵感。经过一番寻找,我们初步有三个方向,分别是用java,python和C#来写,但是java我们两人基本都没有接触过,python我本人并没有接触过,只有结对的潘巍有一些了解,所以用这两种语言实现项目对我们来说有些困难。最后剩下的C#,我们在小学期时使用C#开发过项目,所以相对比较熟悉,故选择了这个。

Part1 PSP

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划3030
· Estimate· 估计这个任务需要多少时间2010
Development开发15day13day
· Analysis· 需求分析 (包括学习新技术)400180
· Design Spec· 生成设计文档3060
· Design Review· 设计复审 (和同事审核设计文档)2010
· Coding Standard· 代码规范 (为目前的开发制定合适的规范)1010
· Design· 具体设计6040
· Coding· 具体编码600500
· Code Review· 代码复审4day1day
· Test· 测试(自我测试,修改代码,提交修改)600600
Reporting报告145380
· Test Report· 测试报告100300
· Size Measurement· 计算工作量1520
· Postmortem & Process Improvement Plan· 事后总结, 并提出过程改进计划3060
合计111809400

Part2 Information Hiding, interface design, loose coupling

2.1 Information Hiding

信息隐藏是把数据结构与实现过程放在一起,使得相关内容彼此靠近,对外提供相对完整,独立的功能,对隐藏信息的访问只能通过接口进行操作。信息隐藏提高了软件的可修改性和重用性,因为修改涉及的是模块内部,避免与外部的交互,这样使得修改的影响面局限于一个较小的范围之内

在本项目中,所有的隐藏信息都无法由用户进行直接的改动,而是必须要通过接口才可以访问,很好地完成了信息隐藏。

2.2 interface design

接口(软件类接口)是指对协定进行定义的引用类型。其他类型实现接口,以保证它们支持某些操作。接口指定必须由类提供的成员或实现它的其他接口。与类相似,接口可以包含方法、属性、索引器和事件作为成员。

2.3 loose coupling

耦合度说明了模块间的依赖所造成的负面影响。松耦合的核心就是解决负面影响导致的“不良依赖”,其目标是最小化依赖。在本项目中,我们的各个模块都较为独立,一个接口的调用不会对其他模块执行造成太大影响。不过一些公共变量的使用使得公共耦合较多,这一点有待改进。

Part3 接口设计

3.1类定义

在本项目中,我们设计了三种基础类,Elevator类,Customer类和ChangeData类,即电梯类,乘客类和电梯数据改变类。

Elevator类的成员变量包含电梯可去楼层,人数限制,承重等,成员函数为构造函数。

Customer类的成员变量包含乘客的体重和进出电梯时间,成员函数为构造函数。为了测试的方便,我们在Customer类加了一个临时的成员变量:TargetFloor,即目标楼层,采用随机数生成,这样可以在我们还没有实现“按电梯内按钮决定目标楼层”的功能时来确认电梯能否正常运行。

ChangeData类的成员变量为电梯的一些参数,用于实现电梯参数的手动调整。

3.2函数定义

除了类之外,我们还设计了几个核心函数,包括bus调度算法,电梯驱动,电梯接乘客,送乘客以及更改电梯标识颜色的功能,此篇博客主要叙述电梯接送乘客以及更改标识颜色,调度算法放在下一篇博客讲述。

我们设计了几种核心函数,具体叙述如下:

3.2.1 电梯核心运行函数Bus

Bus调度算法意如其名,就是让电梯如同公交车一般运转,每一站都要开门和关门,如果该层有人上电梯或下电梯则执行相应的接人或送人函数,直到运行到顶楼或底楼,改变行进方向。

四台电梯的运行,我们考虑使用多线程来模拟。在程序开始执行后,创建四个线程,每个线程代表一个电梯,通过upFlag[],downFlag[]等全局变量来协调电梯的运行。

3.2.2电梯驱动函数runElevator()

我们将各线程创建时执行的函数命名为runElevatori()函数,该函数负责电梯的运行流程。伪代码如下:

电梯状态为 ‘上行’
当前楼层为 ‘1while(true)
    if(电梯向上运行)
		if(电梯未到顶楼)
        	if(电梯可以去该楼层)
            	OpenDoor()
                givePeople()
                getPeople()
                CloseDoor()
                楼层数 加1
        else 
        	电梯状态置为 '下行'
    else if(电梯向下运行)
		if(电梯未到底楼)
        	if(电梯可以去该楼层)
            	OpenDoor()
                givePeople()
                getPeople()
                CloseDoor()
                楼层数 减1
        else 
        	电梯状态置为 '上行'

3.2.3电梯接乘客getPeople

一开始我们陷入了僵局,那就是如何保存上电梯的乘客及其相关信息。我们初步决定用栈来保存,但发现C#对栈的支持并不好,后来我们考虑使用链表来进行保存,但是感觉还是太过于繁琐,所以陷入了僵局。

后来我提出了一个比较抽象的观点,即我们不需要保存乘客类,因为电梯对此也并不关心,电梯不关心到底是谁要去某一层,它关心的是去某一层的到底有多少个人,和这些的人体重一共有多重。于是我们在Elevator中添加了两个成员变量perFloorNum[]以及perFloorWeight[],用于记录每一层有多少人要去,而这些人又总体重为多少。这样,就解决了问题。

然后是具体设计接人函数,主要流程为

在这里插入图片描述

3.2.4电梯送乘客givePeople

接上文,送人函数givePeople()也是通过成员变量perFloorNum[]以及perFloorWeight[]来执行的。在执行该函数时,只需用电梯内的总人数减去perFloorNum[nowFloor],用电梯内的总质量减去perFloorWeight[nowFloor],这样就实现了下人的过程。

3.2.5更改颜色函数

与现实中相似,在电梯按钮按下后,按钮会改变颜色。

对于电梯请求按钮,如果被按下,则按钮变成红色,如果未被按下,则按钮变成白色。

对于电梯运行情况按钮,电梯运行到该楼层时,电梯为黄色,开门时为红色。电梯未运行到该楼层时,电梯为白色。

一开始我们对于如何改变按钮颜色没有什么想法,我在网上查阅资料后,尝试了好几种方法都失败了。最后我们决定使用最基础的方法,给每个按钮按意义命名,然后遍历所有按钮,通过按钮名找到自己想要改变颜色的按钮。

Part4 UML图

在这里插入图片描述

Part5 Design by Contract, Code Contract

契约式设计或者Design by Contract (DbC)是一种设计计算机软件的方法。这种方法要求软件设计者为软件组件定义正式的,精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。它的优缺点如下:

优点:

  • 使用者和被调用者的地位是平等的,双方必须彼此履行义务,才可以行驶权利。
  • 调用者必须提供给被调用者正确的参数,被调用者必须保证正确的结果和调用者要求的不变性。
  • 保证了双方代码的质量,大大提高了软件工程的效率和质量。
  • 契约关系的双方是平等的,没有哪一方可以只享有权利而不承担义务。

缺点:

  • 强行使用会造成代码的冗余和不可读性的提高。
  • 契约式编程并未被标准化,因此项目之间的定义和修改各有不同,会造成代码的混乱

我们在调用接口前,对调用的条件做了条理清晰的规定。每个函数也都有明确的结束状态,不会永远循环下去,具体的实现见代码。

Part6 程序代码规范,设计规范

我们达成共识的程序代码规范,设计规范为:

(1)变量和控件命名使用驼峰命名法。

(2)变量和控件的命名不能随意,必须能够清晰表示他的功能,同一类的控件必须有同一类的命名形式。

(3)在重要函数和命名意义不太明确的变量前写明详细注释

(4)格式要符合规范,缩进换行等要严格把控。

程序产生的异常及其解决方法:

(1)程序发生了数组越界。我们通过调试发现,我们在存储-1楼的信息是,访问的数组下标为-1,于是我们对于所有的数组型变量,均用下标i+1来存储i层的信息,即-1楼信息存储在[0],0楼信息存储在[1],以防止数组越界。

(2)我们发现在一开始的Bus调度算法测试时,电梯无法在发出请求的楼层接到人。我经过阅读代码最后发现问题,我们在接人时会将对应的楼层请求按钮置为白色,并将数组upFlagdownFlag对应的部分置为0,但我们的置0太早,导致电梯还没有接人就已经置0了,因此电梯接不到人。修改了流程后,电梯能够正常接人了。

(3)电梯在UI界面中不会提示经过0层,我们经过检查代码发现,在颜色变化时楼层+1才是需要修改的位置的下标,将其修改我们便修复了该bug,并在所有容易犯类似错误的地方都进行了检查。

Part7 UI界面设计

7.1系统主界面图:

在这里插入图片描述

我们将UI界面分为三个部分,分别称呼为“电梯内”“电梯外”“监控室”。

7.1.1电梯内

最左边是“电梯内”,共有四页,每页代表一个电梯内的按键,用来表示当前上电梯的乘客的目标楼层。这一部分与上次的改动为,增加了三个用来显示文字的框。原因如下:

我们在使用Bus调度算法执行电梯调度时,为了测试方便,每层的人数和要去的楼层都是由随机数生成,“电梯内”的按钮并没有实际的作用。但是为了实现SSTF,LOOK等调度算法,肯定不能像以前一样,我们需要让“电梯内”的按钮也实现他的作用。我提出初步想法后,经过两人讨论,最终决定实现过程为:

(1)电梯运行到有外部电梯请求的楼层时,停下(线程睡眠)。

(2)”电梯内“的每页22个按钮对应着你想去哪一层,每当用户按下一个按钮,就代表一个乘客进入电梯发出请求

(3)随机生成乘客的体重并将体重显示在下方的文字框内,第二个文字框显示电梯内现在的总体重。

(4)判断该乘客能否上电梯,如果不能上,则让该乘客退出。如果能上,则在对应的电梯成员变量上做相应操作。

(5)如果按动最下方“运行”按钮,则电梯执行关门程序,并重新开始运行(线程唤醒),否则返回第二步

7.1.2电梯外

中间是“电梯外”,意在标识第几层有人按电梯请求按钮,并标明是想上楼还是下楼,若再次按按钮,则会取消请求。例如按下f16的上行按键,则会调用对应的函数,将upFlag[16+1]置为True,并调用改变颜色的函数,将该按钮颜色置为红色。若是再按一次这个按钮,则会调用对应的函数,将upFlag[16+1]置为False,并调用改变颜色的函数,将该按钮颜色置为白色

电梯参数修改和显示的按钮,点击以后则会弹出相应的窗口,click事件对应的代码示例如下:

Form2 form2 = new Form2();
form2.Show();
7.1.3监控室

最右边是“监控室”,用来监视电梯的运行情况。平常按钮均为白色,电梯运行到某层,对应的按钮颜色变为黄色,若是电梯开门,按钮颜色则变成红色。

右下方有两个按钮,分别为“开始运行”和“停止运行”,能够调用函数使得电梯运行或停止。

7.2电梯参数设置图:

在这里插入图片描述

在点击确定按钮后,将会使用ChangeData类来存储各个文本框中的数值,并将控制修改参数的值置为true,进行参数的修改

7.3电梯信息查看:

在这里插入图片描述

在点击刷新按钮后,会调用Flash函数,继而在各个框中显示电梯的当前信息。

Part8 结对编程

8.1 结对编程的优点

个人编程时难免会失误犯错,但在结对编程中,因为有随时的复审和交流,程序的质量更接近于一对程序员中水平较高的那一位,甚至还会有所超过。这样,程序中的错误就会少得多,程序的初始质量会高很多,会省下很多以后修改、测试的时间。具体来说,结对编程有如下的好处:

  • 结对编程能提供更好的初始设计质量和代码质量,节约修改和测试的时间。

  • 对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。

  • 能够起到互相督促的作用。 当有另一个人在你身边和你做同样一件事情的时候, 你不好意思划水。

  • 在企业管理层次上,结对能更有效地交流,相互学习和传递经验,能更好地处理人员流动。因为一个人的知识已经被其他人共享。

总之,如果运用得当,结对编程能得到更高的投入产出比(Return of Investment)。

8.2 结对编程的缺点

  • 对于有不同编程习惯的编程人员,相互适应是一件很困难的事,甚至会产生大的矛盾。
  • 对于队友的不信任也有可能产生比一个人思考更大的内耗。
  • 两个人如果闲聊,效率可能比一个人还要低。
  • 面对新手,老手可能会感到烦躁。而新手又在面对有经验的老手时可能会由于紧张从而总是出现低级错误,导致恶性循环。
  • 很多人更喜欢单兵作战,自在的编程,结对工作会让他们感到拘束,不放松,无法进入编程状态。

8.3 个人优缺点

8.3.1 个人优缺点
  • 曹非凡:
    • 优点:
      • 对算法有较为良好的了解。
      • 思维活跃,可以提供一些新奇的思路,从多个角度解决问题。
      • 规划能力比较强。
    • 缺点:
      • 自制力不够,容易走神或摸鱼。
  • 潘巍:
    • 优点:
      • 对算法有较为良好的了解。
      • 自制力较强,能够催促对方及时跟进完成任务。
      • 专注,善于沟通。
    • 缺点
      • 粗心,偶尔会犯一些低级错误
8.3.2说服更改缺点

我们采用的是三明治法则。

第一层是认同、欣赏、关爱、幽默感。

中间这一层是建议、批评。

第三层是鼓励、希望、信任、支持。

这种方法,能够让对方在心平气和地情况下认识到自己的错误并乐于改正,避免产生矛盾。

Part9 问题回答

9.1 如何提供足够多的信息给调度器,以便顺利完成调度?

在Elevator类中设置state来表示电梯当前的状态为上升还是下降,设置成员变量nowFloor表示电梯当前楼层,设置成员变量peopleNumweight表示当前电梯人数和当前电梯承重,设置成员变量perFloorNum[]以及perFloorWeight[]来记录每层下电梯的人数和体重和,设置成员变量accessFloor[]aimFloor[]表示可去的楼层和要去的楼层,全局变量upFlag[]downFlag[]用以标识那一楼有人发出电梯请求和请求的类型,从而实现对电梯的调度。

9.2实际驱动电梯的组件是什么?

我们通过线程来模拟电梯,线程创建时调用调度函数给与信号,通知电梯运行,即执行runElevator()

9.3如何规定乘客行为?例如当乘客需要从3层到20层时,但电梯不能直达,乘客该如何行动?

当乘客进入的电梯不能直达目标楼层时,我们规定乘客直接退出该电梯,让其爬楼前往目标楼层

Part10 个人感悟

这次大作业真的是一次很难忘的经历,任务乍一看很难,我和队友都没有头绪,但是在我们的的共同讨论,齐心钻研下,也是一点一点啃下了这块硬骨头。

从一开始什么都不明白,到共同讨论,然后共同查阅资料,再到一起修改测试,真的感觉学到了特别多,虽然这期间也出了很多的差错,但是总体来说,还是非常开心的。这次大作业让我蜕变,更加直观的参与到项目开发中来,非常感谢老师。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值