软工_结对项目总结博客

GitHub地址:https://github.com/shihaoran/WPFUI_Metro

关于结对编程

  第一次进行真正的结对编程,而且我们组又是最奇葩的三人组合(14061183韩青长)(14061195陈彦吉),在经历了三天的合作以后,感觉收获还是蛮多的,下面是我对于结对编程的一些个人体验。不过介绍体验之前,先上一张我们组结对的照片吧!

优点

  • 在结对编程的过程中,两个人共同面对同一份代码,编码时旁边时刻有人提示监督。这样写出的代码,首先考虑的特殊情况会更多,能避免很多一个人编程时因为考虑不周而在某个不起眼的地方产生的Bug,代码质量更高,少了很多调试时间。
  • 同时,由于两个人交替工作,一方面可以缓解疲劳,同时又因为身旁有人共同工作,能避免很多的偷懒和注意力分散的问题。
  • 结对编程时是两个脑子在思考,可以集思广益,各自提出自己的想法,再相互比较,以至于共同完善各自的想法。这样可以写出质量更高的代码。同时编码时也可以学习对方的优秀编程习惯。

缺点

  • 结对编程时,有时有一些完全可以并行工作的任务,这时两个人如果还是一直一起编程的话,可能会效率比较低。
  • 结对编程时如果之前就有代码,那么另一个非作者的一方在理解作者代码时会有很大的困难。
  • 结对编程要求双方时间统一,如果要编写大项目,很难有那么多的正好能凑在一起的时间。

 

我的结对伙伴:韩青长同学(14061183)

优点

  • 因为这次结对时代码的基础是我写的,所以他要用大量的时间来理解我的代码。不但没有怨言,而且整个结对过程中配合非常愉快。
  • 非常认真,在我走神的时候看到他都不好意思偷懒了==
  • 虽然没有最后使用他的代码,但是复审时看到真的觉得他的代码有很多需要学习的地方。

缺点

  • 如果非要说的话。。。你的鼠标如果是好的就好了。

 

我的结对伙伴:陈彦吉同学(14061195)

优点

  • 思路很开阔,经常在我和韩青长一筹莫展的时候提出一个新的而且很有用的建议。
  • 代码写的比我好,人家只有四百多行,我都上千了。而且确实我写的思路有点乱,自愧不如啊!
  • 做了所有车站的坐标列表,真的,真的是一个超大的体力活!

缺点

  • 哈哈哈结对中间去健身了一小会

关于设计方法

  在查找这些设计方法的同时,我的内心是有些崩溃的,看着我写的程序,真的就是这些设计方法的反面典型啊。尤其是强耦合这一点,真的让我的结对伙伴超级头大。我们为了解耦花了不少的时间。不过相信通过这次的训练,今后对于程序设计方法上将会更加的熟悉。

information hiding

  当开发一个完整的程序时,可将程序的每个组成部分封装在一个模块中,每个模块尽量少地对外展示模块内部的数据与对数据的处理,以此提高代码的复用性、可维护性。减少外界可见的信息,以保持模块的独立性,以此降低耦合
  从具体实现来说,我们确保某些成员变量和功能为private,并尽可能保证高层类只会调用底层类中特定的几个可供外界访问函数,而将其他方法封装在内部,限制了高层类对底层类的调用。

interface design

  接口设计一共有六大原则。。原则说的都很清楚。。但是具体我们如何使用还是要多摸索。。
  以下整理自博客

  • 单一职责原则

  根据实际需求划分职责,尽量做到职责单一

  • 里氏替换原则

  所有引用基类的地方必须能透明地使用其子类的对象

  • 依赖倒置原则

  高层模块不应依赖底层模块,两者都应依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象

  • 接口隔离原则

  接口尽量小,不要建立大接口,同时接口中方法尽量少,将接口隔离起来,将整体框架进行有效划分。

  • 最少知识原则

  跟information hiding有异曲同工之妙,此原则的使用可以降低程序耦合

  • 开闭原则

  一个软件实体如类、模块和函数应该对扩展开放,对修改关闭,编程前对不同的模块确立明确的逻辑分工,为变化预留位置

  但是我们这一次由于大结构简单,因此没有设计接口,所以这些原则没有得到很好的实践。。希望能在以后的团队项目中积累相关经验

loose coupling

  尽可能全面地把握耦合度的高低,平衡好耦合和内聚程度,以确保可读性、服用性、可维护性的提升
  不可一味强调低耦合,例如程序中发生变更概率很小的地方,不宜强行低耦合。
  还有程序中一些为大函数服务的小函数,小函数本身是依附大函数存在的,本身不会和其他模块建立过多的依赖关系,此时在耦合度上最好也做相应折衷

契约式编程

  design by contract把类与被调用类之间的关系看作契约,规定了双方的权力与义务,这一方法被认为是构建OO软件系统方法的核心。
  另外,
  我们通过一些知乎回答了解到的契约式设计:
  

  目的:
  在设计程序时明确规定一个模块单元在调用前后应当处于何种状态,属于一种设计风格或是语法规范
  思路:强调前置条件、后置条件和不变式,当违反这些操作时程序会抛出异常。

  优点是:契约式编程使代码标准化、规范化,提高了程序工程化程度。
  同时,对于测试者,在我们这次作业中,我们通过在不满足前置条件的情况下抛出异常,便于测试者测试。

  缺点是有点麻烦。。

 

单元测试与代码覆盖率

 

 

  这次结对编程,由于函数之间的调用关系较为复杂,因此我们仅对最主要的几个函数进行了测试如下是测试函数 。
 
  一般来说通过assert.areequal方法可以测试大多数运算操作的值是否正确
  [TestMethod()]
  public void ReadDataTest()
  [TestMethod()]
  public void BuildGragphTest()
  [TestMethod()]
  public void FindNearestTransStaTest()
  [TestMethod()]
  public void DijkstraPathTest()
  [TestMethod()]
  public void BFSPathTest()
  [TestMethod()]
  public void MakePathSectionTest()
  [TestMethod()]
  public void isSameLineTest()
  [TestMethod()]
  public void GetLinkedStationsTest()
  [TestMethod()]
  public void GetShortestLinkedStationsTest()
  另外,我们也测试了功能函数在遇到错误的输入时是否按照规定情况抛出了我们事先设定的异常,
  我们设定了如下的异常:
  1.某些情况下操作返回的空值引发的空引用异常
  2.站点名称输入错误的站点输入异常
  3.地图文件没有被正确开启的文件未被打开的异常
  4.地图文件内部格式不符合规范的异常
  5.内存溢出的异常

 

UML类图

  这是VS自动生成的类图,不过看上去并没有什么卵用

 

关于算法和UI设计

算法设计

  这可能是我之前比较自信的一部分了,不过没想到在结对最开始的时候我们其实一直在改我自己程序里之前的Bug,虽然不多但是真的非常难改啊!之前复审我代码的陈彦吉同学也说到了,我之前的代码因为采用了“用代码量换效率”的编码方式,三种路径计算用了三种不同的算法,背后需要的基本数据也不能公用。

  也如我个人项目博客中说到的,我从最开始编码的构思阶段,到中途的实现阶段,再到最后的代码优化阶段,一直不停的在推翻我之前的算法,应用更新的,更高效的算法。

  这一方面来讲是一件好事,因为确实效率不停的在提高:采用换乘站来抽象线路,搜索空间直接下降到了原来的20%,在巨量迭代下,整体效率提升巨大。利用迪杰斯特拉算法替代广搜在换乘站的图中进行寻路,又将时间开销下降了一个数量级。陈彦吉同学说我的算法比他快了50倍,我自己都没想到,本来也就觉得能快10倍左右的。

  在另一方面来说,正如陈彦吉同学指出的,我的代码真的非常的长!而且是强耦合的模块化。这点我承认,因为光核心计算算法就有三个,奇葩的数据存储结构又需要用大量的辅助函数来对数据计算前的前期处理以及计算后的结果输出。其实这本来不是耦合性那么强的借口,但是我写的那些辅助函数为了节省代码量全部都进行了公用,在三种算法中都对几个辅助函数进行了反复调用,而且有些不便传值的数据:如地图等都直接写成了全局变量。放眼望去,真的是非常的冗长而且难以理解。

  确实是因为之前没有开发这么长的应用程序的经验(最后看来整个程序接近3000行),感觉每时每刻都在不停地试错,最后完成的时候再回望最开始对于整个架构的设计,真是naïve。从现在来看,下次对于程序架构的设计应该更多的考虑模块之前的耦合程度,尽量做到松耦合和全面的模块化,尽量从一开始就选定好一个通用的数据结构,更多的考虑程序的扩展和维护的方便,写更多的注释让其他阅读程序的人能够读懂……凡此种种,这么多的经验教训,想来也是我这次个人项目和结对项目中的收获与进步吧。

  关于算法的创新与效率上的提高,这两点我的个人项目总结也都写了,在开始结对项目之前,我也刚刚完成了个人项目的附加题(虽然要算将近一分钟),在这次作业中,我想我们组可能是为数不多的几个完成了三种搜索算法的组吧;)

UI设计

  这是我最骄傲的一部分了。个人给UI打90分!

  先上效果图

 

  设置面板

 

  路径显示面板与动画

  之前从来没有做过UI,不过也算看过很多UI了,自诩还有一些审美水平的我当然不想做一个会被人吐槽“丑”的UI。在了解了一下C#的各种UI制作方法后,我抛弃了陈年老旧的WinForm,选择了更加新颖现代的WPF来进行UI的制作。

  WPF的编程语言跟网页的代码很相像,由于之前因为想要美化博客,学了些CSS,所以感觉上手还算比较容易的。磕磕碰碰写了一晚上,终于大概搞明白了UI是一个什么东西,大概怎么写的。

  不过WPF自带的控件也不见得要比WinForm要好到哪里去,所以我又找到了一套Metro风格的自定义控件MahApps,这套控件的设计语言简洁清新,实在是很符合我的口味。

地图支持缩放移动

  当然,最重要的还是关于地图的显示了。

  这里我最开始想过要自己画出地图,因为这样的话地图是矢量的可以更方便的支持放大和缩小,不会失真。而且坐标什么的也比较好定义。不过这真的是超出了我的能力范围,最后权衡取舍,我们还是选用了现成的地铁图的方案。

  地铁图有了,不过要怎么展示呢?这么大的地铁图,如果铺满窗口的话会显得非常小,而如果放到1:1大小的话,要是用滚动条进行拖动就非常麻烦。这点困扰了我很久,最后经过查询网上的资料,终于找了了用WPF自带的相对坐标变换组对象,在其中添加了对于鼠标滚轮和鼠标拖动对应的事件,实现了鼠标滚轮缩放和鼠标拖动移动位置。简直跟在线地图中的实现一模一样,虽然说起来简单。后台代码也有一百多行呢。恩这个功能可以吹一下!

  再就是对于动画的显示,主要思路是根据后台返回的结果在查询对应站的坐标,然后后台新建响应的控件在地铁站的坐标上。这里对于换乘的站点进行了特殊标注,一共有两种不同的图标。

关于多线程和异步处理

  这说起来简单,但是由于是动画,一定有一定的展示时间,所以就免不了在线程中进行休眠,在这个时候,如果用UI线程去进行这个工作的话,整个界面是停止响应的。这时用户如果想要放大缩小移动地图也完全不可能,所以唯一的做法就是新建一个线程专门负责动画绘制。恩看起来也不怎么难。但是。。。

  C#很坑爹啊!只有UI线程才能操作UI啊,子线程操作UI直接崩异常啊!但是UI进程又不能去处理这个动画绘制工作啊!怎么办,我找了很多资料,终于找到了一个叫做backgroundworker的东西,他可以每完成一个任务报告一下进度,然后让主进程来进行一部分处理,然后继续完成下一个任务。这样的话,直接把任务设置为休眠一段时间,然后再提示主进程来画一个控件,然后子进程继续休眠,如此循环直到绘制完成。这才把我的问题解决。同时,我还提供了清除路径按钮,如果正在绘制动画的时候按下就会停止当前动画并清除所有之前的路径,当然绘制完成以后清除也是可以的。

 

实现后台进行长时间计算,同时前台并行处理

 

  再说另一个坑爹之处,之前也讲到我的遍历全图算法要进行一分多钟,而在这个时间总不能让用户傻等着吧。所以我希望能够后台执行查找算法的同时,前台能够继续进行其他的路径计算,等到获得了查找结果再通知用户并显示出来。这里就用到了异步的概念。好在C#很强大的提供了await这个关键字,能够在执行到此时直接返回,等到获得了结果再执行await下面的语句,这简直是神器啊!用上了这个功能,我的程序就能同时执行两个查找了,一个在前台执行需要时间短的长度和换乘优先查找,然后把结果展示给用户,然后后台执行耗时长的遍历全图查找,最大效率的节省用户的时间,在查找算法效率很难提高的前提下尽可能的提升用户体验。恩这可以吹一年!

  然后呢在UI中我也尽力添加了一些小功能,比如切换动画速度啊,关闭动画啊,切换起点站终点站啊,尽量做到完美。

 

关于附加题

 

  附加题的方面呢,之前也说道因为我之前的程序对于机场线这种单行线的处理是不能扩展的,所以我们用了很大的工夫重构之前的整个核心算法。最后实现了完全与地图无关的计算算法。如果要想让程序支持新的城市的地铁,只需要按照我们的读入格式新建一个数据文件,然后再载入一幅地图,根据地图上的坐标记录站点的位置。程序在放大缩小时会自动支持坐标变换。

  下面是给出的示例格式

  第一项是站点ID,第二项和第三项是站点坐标,第四项是站点名称,第五项是换乘线路。

  理论上来说,只要按照这个格式读入,程序将都能支持。具有可扩展性

 

BEGIN
环线
双向
10号线
000 480 521 巴沟
001 512 512 苏州街
002 565 509 海淀黄庄 4号线
003 609 512 知春里
004 689 509 知春路 13号线
005 744 512 西土城
006 789 512 牡丹园
007 834 512 健德门
008 882 509 北土城 8号线
009 935 512 安贞门
010 986 509 惠新西街南口 5号线
011 1074 509 芍药居 13号线
012 1144 518 太阳宫
013 1151 552 三元桥 机场线
014 1155 593 亮马桥
015 1155 625 农业展览馆
016 1155 656 团结湖
017 1151 695 呼家楼 6号线
018 1155 722 金台夕照
019 1151 746 国贸 1号线
020 1155 830 双井 7号线
021 1155 847 劲松
022 1155 866 潘家园
023 1155 884 十里河
024 1144 930 分钟寺
025 1084 947 成寿寺
026 988 944 宋家庄 5号线 亦庄线
027 944 947 石榴庄
028 892 947 大红门
029 847 947 角门东
030 791 943 角门西 4号线
031 719 947 草桥
032 667 947 纪家庙
033 608 947 首经贸
034 562 947 丰台站
035 521 920 泥洼
036 517 881 西局 14号线西
037 517 818 六里桥 9号线
038 517 788 莲花桥
039 517 746 公主坟 1号线
040 500 710 西钓鱼台
041 473 670 慈寿寺 6号线
042 477 630 车道沟
043 477 591 长春桥
044 477 552 火器营
END
BEGIN
非环线
双向
13号线
045 716 617 西直门 2号线 4号线
046 694 565 大钟寺
004 693 508 知春路 10号线
047 694 472 五道口
048 694 437 上地
049 693 379 西二旗 昌平线
050 735 355 龙泽
051 784 355 回龙观
052 847 351 霍营 8号线
053 988 351 立水桥 5号线
054 1076 387 北苑
055 1075 448 望京西 15号线
011 1075 510 芍药居 10号线
056 1076 554 光熙门
057 1076 585 柳芳
058 1058 618 东直门 机场线 2号线
END

 

 

个人总结

 

  数不清在三个人一起在电脑前趴了多久。

  虽然有些摩擦,

  但是我们的程序真的开始跑起来的时候

  每天都要更新几个版本

  又要多改几个Bug

  看到它日臻完美

  真的

  有这样的

  与你一起并肩奋斗的队友

  真好。

 

posted @ 2016-10-02 20:04  shhr  阅读(436)  评论(6编辑  收藏  举报