结对编程组员:
马辰 11061178
柴泽华 11061153
p.s.由于刚开始只在柴泽华的博客上进行修改,所以我的博客提交时间有点问题。。
1) 照至少一张照片, 展现两人在一起合作编程的情况。
结对编程的优点
1)在编程过程中,任何一段代码都不断地复审,同时避免了将写代码的责任抛给一个人的问题,而是属于两个人,可以帮助建立集体拥有代码的意识。
2)结对编程的过程也是一个互相督促的过程,每个人都可以监督,督促对方的工作。由于这种督促的压力,使得双方都可以更认真地工作,频繁交流讨论对方的代码以防止出现纰漏,提高自身的代码质量。
3)为了避免一个人长时间进行同一项工作会产生疲劳的现象,结对编程中两个程序员还可以互换角色,轮换驾驶员和领航员的角色,从而长时间保证较高的工作效率。
结对编程的缺点
1)如果两个人的共同时间比较少的话,互相等待,影响效率,采用结对编程的方式反而比较浪费时间。
2)如果是一个需要花时间长期钻研的项目,采用结对编程的方式反而会影响一个人的思路,适得其反。
柴泽华的优缺点
优点 | 缺点 |
思维活跃,想法新颖。 | 想问题不够细致 |
能很好的和其他人进行团队合作。 | |
能够虚心听取partner提出的意见 |
马辰的优缺点
优点 | 缺点 |
耐心细致 | 沟通能力有待提高 |
肯动手 | |
爱思考 |
2) 看教科书和其它资料中关于 Information Hiding, interface design, loose coupling 的章节并说明怎样利用好这些设计方法。
information hiding(信息隐蔽)
每个模块应该对其他所有模块隐蔽自己的设计决策。换句话说,模块应该规定并设计成为在模块中包含的信息(算法和数据)不被不需要这些信息的其他模块访问。
通过定义一系列独立的模块可以得到有效的模块化,独立模块相互之间只交流实现软件功能所必需的那些信息。还可以加强对模块内过程细节的访问约束和对模块所使用的任何局部的数据结构的访问约束。
在随后软件维护过程中,修改过程中不小心产生的错误不至于传播到软件的其他位置。
interface design(接口设计)
接口设计包括三个重要的元素:
1.用户界面(UI)
包含美学元素,人机工程元素和技术元素,通常UI使整个应用体系结构中独一无二的子系统。
2.和其他系统,设备,网络或其他信息生成者或使用者的外部接口。
需要关于发送和接收信息的实体的确定信息。在所有情况下,这些信息都要在需求工程中收集,并且开始时还要检验这些信息。
3.各种设计构件之间的内部接口。
分析类的设计实现表示所有操作和信息传递模式,使得不同类的操作之间能够进行通信和协作。每个消息的设计必须提供必不可少的信息传递和已被请求操作的特定功能需求。
loose coupling (松耦合)
在设计模型内,协作应该保持在一个可以接受的最小范围内。即通常,一个子系统内的设计类对其他子系统中的类应仅有有限的了解。一个类中的方法应该只向周边类中的方法发送消息,而不是直接的套用周边类中的方法内部的内容。
这些方法在本次工程中的应用
这三个概念是设计面向对象程序的重要策略,它们相互协同,是程序更接近现实世界模型。
面向对象程序设计的关键在于,它能够将程序中的类与真实世界中的种类或者概念较好地实现对应关系。
所以,在程序修改的时候,就像真实世界一样以种类(对象)为个体进行变化,总的修改量和难度会大幅下降。
在这次的工程中,信息隐藏主要运用了设置变量和方法的使用权限,例如电梯的capability是私有的,不能够直接进行访问,只可以通过相应的方法进行访问。如果对象不知道该方法,就不可以进行访问,从而将信息进行隐藏。
接口的设计,主要用于对象之间的交互(通信),在类中的方法,如果访问权限是public,那么就相当于所有人如果知道该方法,都可以进行访问。例如我新增了一个接口bool isfull { get; set; },可以获取电梯是否满载,这样电梯调度算法可以随时知道电梯的是否满载,而不需知道电梯的容量或余量,既完成了通信,也可以更好地隐藏数据。
松耦合,就是在对象之间既要保持一定的通信,也要有一定地独立性。但在程序设计中不能一味地追求松耦合,这样会使程序粒度过低,使得程序庞大繁杂,适得其反。
在这次的程序设计中,Scheduler的算法不能仅仅适用于测试用例,如果电梯的部数变化,场景变化,Scheduler也不用修改,因此,即使知道现在的电梯是4部,也不能在算法中直接使用4,而是应该使用电梯的Count()值。并且算法的实际也不能仅仅使用于测试案例,应该有更大地适用范围。
Scheduler和Elevator类虽然密切相关,但是二者还是主要通过通信来相互控制,并没有直接进行引用。
P.S. 对于Scheduler中直接修改电梯的HistoryDirection方法并不是很好,它迫使Scheduler必须在电梯停止的时候才能进行控制,否则就会出错。
我在Elevator类中新增了一个afterhistory属性,保证了电梯停止的原子性,增强了电梯和Scheduler的通信,使得Scheduler在任何时刻都可以控制电梯的行为,使得电梯算法的实现范围更大,更能适应变化,从而提高运行效率。
3) 描述这些契约式设计做法的优缺点, 说明你是如何把它们融入你的作业中的。
契约式设计的提出,主要基于软件可靠性方面的考虑。可靠性包括正确性和健壮性,正确性指软件按照需求规格执行的能力,健壮性指软件对需求规格中未声明状况的处理能力。
优点:
- 文档。在文档中不仅包含签名描述,也能包含契约描述,如果能更进一步象VisualStudio智能提示那样呈现,能减少很多编码过程中犯下错误。
- 测试、调试、品质保证。NUnit是比较完善的断言测试框架,如果语言本身提供契约式设计支持,我们可以使单元测试中断言的覆盖范围更广泛(考虑一般都是开发者自己做单元测试),能够帮助发现更多隐性的缺陷,断言测试代码的编写会更简洁。
缺点:
契约式设计过于死板,不容易让创意得到实现,也不容易添加新的功能,因为函数功能已经约定好。
在作业中我们应用契约式设计主要体现在对用户输入数据的接收上,这就好比商业契约,用户和程序员之间呈现一种相对平等的关系,即如果用户没有遵守程序员在程序开头所定下的准则,那么程序员所编写的程序也没有义务必须给用户呈现一个满意的结果。
契约式设计在本次工程中的应用
刚开始我和柴泽华对算法进行了讨论,确定了算法的主线。之后,为了达到契约式设计的目的,我们进一步细化算法,几乎以伪代码的方式实现了算法,这个伪代码就是我们的契约。
在实践中,我们发现,契约式设计使得代码的编写一次成型,貌似很好。但是,这个契约式的代码的结果并不理想,当我们想对它进行小规模地修改时,发现它牵一发而动全身。最后不得不重新写一份代码,以获得良好的结果。
可以看出,契约式设计虽然保证了正确,但是对于不断适应外部新环境的开发,以及探索地、创新地开发并不适用。而现在软件开发所面对的环境就是一种不断变化的环境,契约式设计可能会逐渐不能适应。
4) 通过截屏显示你是如何用VS 的unit test 来保证你写的类的质量的。显示unit test 对你的写的类(class) 的覆盖率
单元测试主要步骤是参见邹欣老师的博客,详细步骤如下————
1.create a test project
2.create a test unit in the test project you just created
3.write the test code
using Scheduler; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using commons; using System.Collections.Generic; using Elevator; using World; namespace mc1 { [TestClass] public class UnitTest2 { [TestMethod] public void TestMethod1() { //NaiveScheduler target = new NaiveScheduler(); // TODO: Initialize to an appropriate value Program program = new Program(); program.WorldInit("C:\Users\T\Desktop\now\Loader\elevators.xml", "C:\Users\T\Desktop\now\Loader\passenger2.xml"); program.TickGoes.Join(); //Assert.IsTrue(target != null); } } }
按道理,单元测试应该将所有的可能初始化条件都写在test code里面,但是我为了方便,将program类编程public访问,直接通过运行,检查类的覆盖率。
(问题:老师,这样的单元测试可以吗?)
4.Build and run the test code
5.Analyze the code coverage
6.Analyze the covered percentage and give the solution
Solution 1: go to step 3 to write a better test code
Solution 2: if the test code couldn't be better , MAKE your SOURCE code BETTER!
7.执行Solution1,修改了测试用例。
7.总结
最后类的覆盖率达到了90.32%,结果还是不错的。
刚开始我用的是一个1000人的测试文件,覆盖率是87.14%.
之后用了一个5000人的测试文件,覆盖率是90.32%,竟然有些代码还是没有被执行到,让人意想不到。
说明类中的一些代码是多余的,还不够精简。
从这一次的过程可以看出,单元测试还是要自己设计一些场景,这样对代码的覆盖率和检查效果要好于大批量的测试。
但也可以看出,利用代码覆盖率进行代码测试的弊端,虽然每一句的执行是正确的,但不保证组合的结果是正确的。
因此,对代码进行测试既要采用小型场景设计测试,也要用大量的数据进行组合地测试。
TIPS for writing test code
It's quit difficult for a first learner to write a test code lonely.
BUT the VS is very useful!
You can firstly generate a test framework,
Then rewrite the line with TODO after it,
try many times, you'll konw how the code test go,
Finally,you can write a better one by yourself!
5) 画出UML 图显示各个实体之间的关系 (画一个图即可)
由于类图最能表现出各个实体间的关联,因此我选择用vs2012中的反向工程工具由代码生成类图。
如下图:(由于图形过大,为便于截图,因此缩小的倍数较多)
6) 实现你的算法并说明你的算法的关键 (不必列出源代码), 以及独到之处。
算法的关键:
每个时刻,会统计所有未处理的上行请求和下行请求,存在upwait和downwait数组中。
同样在每个时刻(包括电梯运行中和停止中),每部电梯选择,可以保持循环运行(先上后下再上的循环)的前提下,离自己最近的请求进行执行,同时将这个请求通过加锁的方式,使得其他电梯不能进行选择,这样就不会出现多部电梯同步运行的低效过程。
独到之处:
此算法的独到之处
1.通过增加Elevator的afterhistory属性,使得Scheduler可以每个时刻进行控制,所以算法可以更快地响应变化,效率增强;、
2.通过每部电梯选择最近的请求进行应答,提高电梯载人效率;
3.通过设计加锁算法,使得多部电梯不能响应同一个请求,避免了多部电梯的同步运行(非常低效);
4.通过算法,使得电梯尽量沿一个方向进行运行,直至该方向没有请求再返回,这样可以使电梯的总路程下降。
(关于第4点,在现实中,电梯的载人效率和电梯的功耗都是应该考虑的,这样的算法更贴近现实需求)
下面是我们的算法跑3组测试用例的结果(此时电梯初始层分别是0,19,20,1)
passenger1.xml测试结果 72.45
passenger2.xml测试结果 199.86
passenger3.xml测试结果 218.33
7) 总结
通过这次结对编程,我们都认识到了团队合作的重要性。两个人合作编程,不光可以提高代码的准确度以及效率,迅速发现对方的漏洞,更重要的是,还可以提升双方的水平,得到更好的锻炼。这次的题目也非常的有趣,给了我们思维很大的发散空间,评分形式更是激励着我们不断去思索着更迅速的算法。
感谢老师给予我们这次自主学习,自我锻炼的机会!