截止时间:2016年10月2日24:00。
在第一个作业中,我们用各种语言实现了一个命令行的地铁出行路线规划小程序。接下来,我们看看如果要把小程序升级为能稳定运行,解决用户问题的软件,应该怎么做。
在我们做测试的过程中,我们可以比较具体的地铁站点的名字,以验证算法的正确性, 但是我们实际上并不在乎这个具体名字是“知春路”, 还是“惠新西街南口”,我们只要知道它们相同就好了。能否给每个地铁站点一个数字的标识,这样便于比较?
第一阶段目标:抽象
请修改你的beijing-subway.txt地图文件,给每个地铁站点一个唯一标识ID,使用ID来表示地铁站点之间的连接关系,然后再修改程序中读取文件的部分,让它能处理ID,而不是仅仅车站名字。修改程序之后,请保证程序依然能够完成它原有的功能。之后再签入代码。
大家写的代码都各有特色,大家写的 “软件” 也有一定的用处。如果我们要把这个功能放到不同的环境中去(例如,命令行,Windows 图形界面程序,网页程序,手机App),就会碰到困难,因为目前代码的普遍问题是代码都散落在main()函数或者其他子函数中,我们很难把这些功能完整地剥离出来,作为一个独立的模块满足不同的需求。
我们看到,不同的代码解决不同层面的问题,有些是内部数据的计算(例如最短路径的查找);有些是和用户输入相关的(例如scanf,cin,图形界面的输入字段),有些是和数据的展现相关的(例如 printf ,cout,println,DrawText),有些是和程序所在平台的架构相关的(例如 main 函数,并不是所有的程序都需要某个特定格式的main)。这就需要我们对软件的架构做一些整理和优化。
建议大家把路线规划的计算功能包装在一个模块中(这个模块可以是一个类 Class, 一个DLL,等等), 为了方便起见,我们叫它 “计算核心”Core 模块, 这个模块至少在两个地方可以使用:
- 测试程序,这个可以是一个命令行的程序,或者是JUnit 的框架,或者是Visual Studio单元测试的框架等。这样,我们在算法层级保证了这个模块的正确性。
- 实际的软件,这是交付给最终用户的软件,有一定的界面和必要的辅助功能。
那么这个Core模块和使用它的其他模块之间是什么关系呢?它们要通过一定的API (Application Programming Interface) 来和其他模块交流。这个API 接口应该怎么设计呢?(这是一个给有一定经验和实力的同学的题目),为了简单,我们可以从下面的最简单的接口开始:
Spath ( )
这个Spath函数接受两个int型ID作为输入(ID是地铁站点的唯一标识),这个模块的返回值是一个int型数组,记录了从一个站点出发到达另一个站点的最短路径的站点列表。
假设我们用的是类,我们的测试程序刚开始可以是非常简单的测试例子:(用伪代码表示)
int[] result = Core.Spath(3, 5);
Assert(result == {3,4,5}); //我们断言从站点3到站点5的最短路径一定是{3,4,5}.
然后同学们实现自己Core 的这个功能。
第二阶段目标:把计算两个站点之间最短路径的功能封装起来,通过测试程序和API 接口测试其功能。
计算两个站点之间最短路径成功之后,然后我们再把之前程序中实现的其他功能也封装成独立的模块,包括读取地图文件、返回地铁线路的所有站点、计算两个站点之间换乘最少的最短路径、输出打印等。由于同学们已经在自己以前的程序中实现了各种算法,这时候只要把实现的算法搬过来就好了。建议大家在每一步都只改一个方面,在每实现一个新的功能的时候,要保证以前运行正确的例子继续是正确的,通过这样的 “回归测试”, 来保证自己实现的函数一直是正确的。(请看书中关于单元测试,回归测试的内容)在确认修改的功能正确之后再签入代码。
第三阶段目标:通过测试程序和API 接口测试对于异常处理的支持。并能看到代码覆盖率。
如果输入是有错误的,怎么告诉函数的调用者“你错了”?建议这个时候,我们要定义各种异常(Exception),让Core在碰到各种异常情况的时候,能告诉调用者——你错了!当然,这个时候,我们同样要进行下面的增量修改:
- 定义要增加什么功能 - 例如:支持 “地图格式错误”异常
- 写好测试用例,传进去一个错误的输入,期望能捕获这个异常。 如果没有,那测试就报错。
- 在Core模块中实现这个功能
- 测试这个功能
- 同时测试所有以前的功能,保证以前的功能还能继续工作(没有回归错误)
- 确认功能完成,签入代码,继续下一个功能
第四阶段目标:图形界面的实现。
我们前面的练习都是基于命令行的,能否做成图形界面呢?首先我们要给每个站点一个坐标信息。
1)请修改beijing-subway.txt文件,加入适当的信息,为实现图形界面做准备。 你要如何设计这些坐标呢?
2)请改进subway.exe,加一个-g的命令行参数, 让它根据你提供的坐标画出各个地铁站,线路,以及换乘站。
我们看到,所有要展现的信息都要存储起来,存储还要有一定的规则,这样程序才好正确地,高效地读出来。存储有下面三个方案:
a) 用文本文件,自定义的格式存放
b) 用XML格式存放
c) 还可以用数据库的方式来存放,例如sqlite
d) 还可以用自己定义的二进制的格式来存放
请比较几种方式的优缺点,特别是,这些方式如何应对变化的内部,外部因素。 例如,在d)方案中,如果要增加一个地铁站,或者修改一个地铁站的名字,我们应该怎么做呢?
3)扩展subway.exe,使其在处理-g参数时,程序在图形界面中不仅能显示地铁地图 (各个站点的相对位置和官方地图类似即可)还能
a) 支持用户选择的出发和到达站点计算出最短路径
b) 支持用户设置偏好,是计算最短路径还是换乘最少的最短路径
c) 根据用户选择的出发和到达站点和用户偏好计算出最短路径(或换乘最少的最短路径)
d) 支持动画展示最短路径,用一个小亮点表示乘客,乘客正在经过的车站就会闪亮,乘客走过的路用不同的颜色标识,同时在适当的地方有数字表明乘客已经经过车站的数目
[附加题]第五阶段目标:使程序支持不同城市的地图。
请让程序能处理上海的地铁地图,或者其它城市的地图。把程序由 “固定处理一个地图” 升级为 “能处理多个地图”, 程序的什么模块需要变化?
博客作业要求:
作业 | 博客要求 (写1个博客,附加题的解法写另一个博客) 博客注明结对编程人员的名字/或学号后3位。 |
|
看教科书和其它参考书, 网站中关于结对编程的章节。例如: | 照至少一张照片,展现两人在一起合作编程的情况。 说明结对编程的优点和缺点。 结对的每一个人的优点和缺点在哪里 (要列出至少三个优点和一个缺点)。 | |
看教科书和其它资料中关于 Information Hiding, Interface Design, Loose Coupling的章节
| 说明怎样利用这些好的设计方法。 | |
看 Design by Contract, Code Contract 的内容: | 描述这些做法的优缺点, 说明你是如何把它们融入你的作业中的。 | |
看教科书中,网上有关 unit test 的内容 http://www.cnblogs.com/xinz/archive/2011/11/20/2255830.html
| 通过截屏显示你是如何用VS的unit test 来保证你写的类的质量的。显示unit test 对你的写的类(class) 的覆盖率 | |
阅读有关 UML 的内容 | 画出UML 图显示各个实体之间的关系 (画一个图即可) | |
实现你的算法 | 说明你的算法的关键(不必列出源代码), 以及独到之处。
把你的代码签入到Github |