项目设计
注意:以下异常主要考虑输入引起,罗列备忘。
输入输出设计
输入采用读取文件的方法,文件格式采用json格式,解析使用nlohmann/json开源库。使用json格式的原因的格式简单,键值对的方式便于以后扩展,当前格式样例如下,其意自明:
[
{
"name":"1号线",
"stations":["苹果园","古城"]
},
{
"name":"2号线",
"stations":["积水潭","鼓楼大街"]
}
]
输出格式固定见题目要求。
异常情况
- 文件不存在
- 格式解析错误
设计思路
任务1
比较简单建立一个线路(line)的list,每个线路又是一个站点(station)的列表即可,遍历线路列表找到找到对应线路,输出即可
异常情况
1.线路找不到
任务2
原理上采用广度优先遍历,首先找到终点站的搜索路径就是最短路径。
注意地铁线路实际存在正反两条,反向路径输入文件中不包括,需要自动产生。
生成一个基于站点信息,站点信息包括下一站,文本化描述如下。
{
station:'复兴门',
next:[
{station: '西单' , line:'1号线', dir:0},
{station: '南礼士路' , line:'1号线', dir:1},
{station: '阜成门' , line:'2号线' , dir:0},
{station: '长椿街' , line:'2号线' , dir:1},
]
}
对这样一个数据结构进行广度优先遍历即可。
异常情况
1.站点找不到
记录
完成以上设计耗时2个小时。
算法设计
构建一个以station为节点的图,图的边是线路名,在图中采用广度优先得到从起点到终点的路径即可。
关键数据结构
typedef struct
{
string routeLine;
shared_ptr<Station> station;
}NextStation;
class Station
{
public:
Station(string stationName);
void setNextStation(shared_ptr<Station> station, string routeLine);
list<shared_ptr<NextStation>> nextStations; //该站的下一站集合
string name_; //站点名,站点的唯一标记
};
站点用name来唯一标识,拥有一个指向下一站的站点集合,下一站是一个二元组(路线,站点),表示通过某线路到某站点。
图构建算法
graph TD
readLine[读入路线 line] --> readStation[获取线路的中的一个站点]
readStation --> setNext[设置上一站点的下一站为当前站点,途经路线line]
setNext --> setPrev[设置当前站点的下一站为上一站点,途经路线line]
setPrev --> condition{当前站是最后一个吗}
condition --NO--> savePrev[将上一站改为当前站]
savePrev --> readStation
condition --YES--> stop[结束]
图搜索算法
- 找到起点站,startStation,设置当前station为startStation。
- 准备3个路线集,bingRotues命中路线集,testRoutes,prepareRoutes。
- 初始化testRoutes,里面只有一条路线,起点终点都是startStation.
- 遍历testRoutes,得到当前测试路线testRoute,初始化prepareRoutes为空。
- 找到testRoute的终点站endStation。
- 构造以endStation的下一站为终点的路线,并归并到prepareRoutes中。
- 遍历完testRoutes中的每一条路线
- 将prepareRoutes中终点为查询终点站的路线放入bingRotues。
- bingRotues为空转4.不为空转10.
- 输出bingRotues中换乘最少的线路。
注:输出bingRotues中换乘最少的线路,非题目要求。
设计修改
测试中发现项目中用到json解析库仅支持utf-8,但是windows默认的字符集是GB,每次转换文件太麻烦,因此更改输入格式为
线路名:站点1,站点2
时间花费
主要记录实现中问题和花费时间。
命令行处理,读入文件,json解析,花费2个小时。
以前都是用的get_opt函数,对get_opt_long不熟,花费时间超过预期。
完成任务1,仅输出到屏幕,站点数据结构构建部分。耗时2.5个小时。
完成任务2,耗时8小时
填表
PSP 2.1 | Personal Software Process Stages | Time |
---|---|---|
Planning | 计划 | 1h |
· Estimate | · 估计这个任务需要多少时间 | 1h |
Development | 开发 | 16h |
· Analysis | · 需求分析 (包括学习新技术) | 1h |
· Design Spec | · 生成设计文档 | 1h |
· Design Review | · 设计复审 (和同事审核设计文档) | 0.5h |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | X |
· Design | · 具体设计 | 1h |
· Coding | · 具体编码 | 10.5h |
· Code Review | · 代码复审 x | 0.5h |
· Test | · 测试(自我测试,修改代码,提交修改) | 0.9h |
Reporting | 报告 | x |
· Test Report | · 测试报告 | 0.5h |
· Size Measurement | · 计算工作量 | 0.2h |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 0.2 |
> | 合计 | 25h |
X为还没有做
单元测试
代码样例
namespace SubwayUnitTest
{
TEST_CLASS(StationManageTest)
{
public:
TEST_METHOD(StationManage_getLine)
{
char* lineName = "1";
Core::readFile(TESTFILE);
auto rt=Core::getStations(lineName);
Assert::IsTrue(rt == "a b c d
");
}
TEST_METHOD(StationManage_queryRouteStartNotFound)
{
map<string, shared_ptr<Line>> lines;
Core::readFile(TESTFILE);
auto rt = Core::getRoute("x", "a");
Assert::IsTrue(rt.first == StartNotFound);
}
TEST_METHOD(StationManage_queryRouteEndNotFound)
{
Core::readFile(TESTFILE);
auto rt = Core::getRoute("a", "x");
Assert::IsTrue(rt.first == EndNotFound);
}
TEST_METHOD(StationManage_queryRouteNotExsit)
{
Core::readFile(TESTFILE);
auto rt = Core::getRoute("a", "n");
Assert::IsTrue(rt.first == RouteDoesNotExist);
}
TEST_METHOD(StationManage_queryRoute)
{
Core::readFile(TESTFILE);
auto rt = Core::getRoute("a", "f");
stringstream ss;
rt.second->dump(ss);
string rtRoute = ss.str();
int x = 0;
if (rtRoute == "4
a
b
2
e
4
f
")
x = 1;
Assert::IsTrue(rtRoute == "4
a
b
2
e
4
f
");
}
TEST_METHOD(StationManage_fileNotFound)
{
auto rt = Core::readFile(WRONGFILE);
Assert::IsTrue(rt != 0);
}
};
}
测试了线路输出,路线查询两种正常情况。 起点不存在,终点不存在,线路不存在,文件不存在,4种异常情况。