文章目录
- 一. 实验目的
- 二. 新的配置内容
- 三. 实验具体内容
-
- 3.1 Poetic Walks
-
- 3.1.1 写测试
- 3.1.2 两种方式实现有向带权图
- 3.1.3 实现泛型
- 3.1.4 Poetic walks
-
- 3.1.4.1 测试
- 3.1.4.2 实现
- 3.1.4.3 附加题
- 3.2 Re-implement the Social Network in Lab1
- 3.3 Playing Chess
-
- 3.3.1 我的实现的结构
- 3.3.2 Board & Piece & Position
- 3.3.3 Action 与 Board 的关系
- 3.3.4 Game & Player
- 四. 总结(建议先看这部分)
一. 实验目的
就如同这个实验的名字一样,这个实验的重点就在于一方面,从给定的问题中抽象出来需要设计的抽象数据类型(ADT),然后利用面向对象编程(OOP)来实现ADT。
在设计ADT的时候,由于从这个实验开始,程序已经逐渐变得复杂起来,像原来一样没有做好计划就直接写代码肯定是不行的。编写代码之前,我们需要首先写好spec和pre/post condition,以及后面的AF, RI等,同时想好对于表示泄露的处理方案。这些非代码部分虽然一开始写起来很麻烦,但是可以帮助我们在开始写代码之前先捋清楚思路,从而减少我们在后续对于代码的修改工作。
当然,比起后续的修改,更加重要的是代码的可理解性。我们今年的lab4中有一个给不写这些注释的代码debug的任务,在做这个任务的同时,我相信很多同学都和我一样,在内心里把那个不写注释的人渣枪毙几十万次了。很多时候,我们自己都看不懂我们之前写的代码。就算是为了避免养成一个会成为未来同僚的杀人动机的坏习惯,也还请一定要好好写注释(笑)。
除此之外,还有测试部分也是在代码之前完成的,也就是TDD。其好处老师在课内已经讲过,我就不再赘述。当然,TDD的前提是你的spec写的很清晰准确,后续修改不大。像我在lab2的3.3和lab3的前面的一些实现中,因为spec一开始写的不太对,而且在写测试的时候也没发现问题,结果就是我之后想改ADT,就得把我写过的测试进行相当大的变动。这一过程还是有些折磨人的。所以建议大家一定要在写测试和代码之前想好整体程序的结构,不要边做边想。
二. 新的配置内容
这个实验里面的配置部分多了一个eclemma,虽然在实验手册里面没有明说让我们用(后面的lab4才提到),但是这个东西从完善测试的角度而言还是很好用的。当然,不要过度追求覆盖度,覆盖度高自然是好事,但是100%的代码覆盖度也不意味着代码就完全地被充分地测试到了。大二的时间很紧,我们在这个实验的行为付诸实践与否,更多地要看我们这样做能学到些什么,为了一个数字而浪费太多时间就得不偿失了。
三. 实验具体内容
3.1 Poetic Walks
这个实验的目的是让我们对一个有向带权图的抽象类的子类先进行类型的构造,再扩展至
在开始说这个实验的具体实现之前,首先要提到一个坑,就是AF,RI这些概念的讲述,是略晚于实验的开始的。所以,如果发现自己在写完测试之后,就再也看不懂题干了,比起求助谷歌翻译或者金山词霸,预习一下之后的ppt可能是更好的主意。
3.1.1 写测试
这个题干的顺序其实就是让我们养成一个TDD的习惯,本身并没有什么难度,重点在于TDD的思路。注意在写测试之前,也可以先用test strategy整理一下思路。
3.1.2 两种方式实现有向带权图
-
ConcreteEdgesGraph
回顾lab1,我们做过一个无向图(虽然当时我们的实现也可以表示有向图),表示这个无向图的时候,我们用的字段就是图的边list和点list。即使换成了带权有向图,也只是代表边的ADT多出来了表示权值的字段而已。相对而言,由于不需要进行广搜,所以对于顶点的类,是否被访问过以及距离之类的字段就可以去掉了,剩下的也就只有一个String类型的名字而已了。既然这样,我们表示图中的每一个点,其实只需要记录它的名字即可。
实现起来虽然多少有些麻烦,但是并没有什么难度,毕竟这个问题只是想要帮我们养成一个良好的编程习惯而已。 -
ConcreteVerticesGraph
比起刚刚那个问题,这个问题要求我们实现点的ADT而不去实现边的ADT,思路无非就是把连着某一个点的边当成了这个点的字段而已,实现起来比起前面那个算是有些难度,但是问题也不大。需要注意的地方是,前面那个问题中,因为边的ADT实现很简单,我们修改一条边的时候,比起对于边的ADT进行修改,倒不如直接废弃掉这条边再造一个新边来的方便,所以是immutable类型。而这个问题中,因为某一个点的ADT中记录着连着这个点的所有边,如果想要再从这个点上连出/连入一条边,就是对这个点的改动。这种情况下,废弃重建的成本就相当离谱了,所以我们这个点的ADT是mutable类型。对于mutable类型,需要进行额外的保护,这也就是这一部分主要想让我们学习到的东西。
3.1.3 实现泛型
这里的修改本身其实很少,把所有实现类里面的String都改成占位符L(toString别改),然后在实现类所有的Edge或者Vertex后面加上
可以说这个问题的难点并不在于修改本身,更多的在于让我们理解泛型这一思维。
3.1.4 Poetic walks
3.1.4.1 测试
先说测试。这里就体现了我刚刚说的,eclemma覆盖度高未必就是比覆盖度低的要好,比如toString方法,测不测试都可以,就以我的测试为例,为了这一句话的覆盖度,要提前猜出toString这么大一串输出(然后用AssertEquals判定是否符合预期),并且手打出来:
实在是得不偿失。同时,在读入输入文件的时候,为了保证程序的健壮性,我们也需要考虑输入文件的至少以下几个可能问题:
- 连续多个空格
- 词中夹带符
- 大小写混乱
- 空行
所以我的测试中使用的输入文件是这样子的:
3.3.2 Board & Piece & Position
首先,我对于这个软件的入手点是从棋盘开始的。我认为,我们对于棋盘可以有着如下两种理解:
- 从棋盘本身的结构来考虑:类似于我们和活人一起下棋时候看到的棋盘,把棋盘当作一个1919或者88的二维数组,每个数组中存放着至多一个棋子。这种情况下,棋子的位置存在棋盘上,而不是棋子的属性。
- 从棋盘的功能来考虑:类似于我们看到的棋谱,棋盘从功能上是用来记录盘面上的棋子个数种类和位置的工具,那么我们就可以用一个列表来记录所有的棋子。棋子的位置作为棋子本身的属性,而棋盘就单纯只是一个棋子的聚合体。
这两种思维方式导致的是不同的类与类之间的关系。从上面的简易UML图可以看出来,我采用的是第二种理解。这样子的好处是遍历全部棋子的时候耗费的时间少(不需要考虑没有棋子的点),但是很显然在直观程度上要差很多。如果想要可视化棋盘,还得在转化成二维数组的形式来存储。所以总体上我还是建议采用第一种思维方式。
3.3.3 Action 与 Board 的关系
对于Action类,我的思路是采取一个Board当作字段,而其中的所有方法都是对于这个Board的操作。那么可能会有人问了,那我还要这Action类有何用接在Board里面实现方法不好吗/p>
从我们目前学到的内容来讲,我们在Board里面实现的方法仅限于对棋盘的基本操作。也就是说,一个棋盘,无论是进行什么棋类游戏,都会进行的基本操作,例如将某个在某位置生成新棋子,移除某位置的棋子或是移动某位置的棋子。而各种不同类型的游戏的操作,例如国际象棋的移子和吃子,围棋的落子和提子,都是对这些操作的组合和扩展。这些扩展操作,就可以放在Action类中,因为这样做并不会影响到Board内部,也方便维护和进一步的扩展。
从我们以后会学到的内容来讲,这个将基础操作和扩展操作的思路,有点类似于5-2的Visitor模式(但实际上并不一样!注意区别!)。Action类似于抽象类Visitor,泛指一切棋类操作,而每个Action的子类型作为一种棋类的全体操作的实现,想要完善某种棋类的功能或者增加新的游戏种类的时候,直接修改或者添加对应的子类即可。
3.3.4 Game & Player
Player这个ADT的实现本身很简单,就是包含了用户名字和所使用的棋子的颜色信息即可。(我这里锁定player1执白,player2执黑,所以会出现在围棋中player2先手而国象中player1先手的情况)
相对而言,Game的实现要复杂很多,甚至可以说包含了一半以上的工作量。由于两种类型的游戏共性操作很多,为了避免重复代码,可以考虑将共性操作提到父类抽象类Game中实现。而个性化的操作就可以分别放在子类中了。仅就我个人经验而言,整个lab2中最令人难受的部分就是一大堆输入的判定,为了提升程序的健壮性,需要把用户当作敲莎士比亚的猴子,对任何错误的输入都要进行错误提示以及要求用户进行重输。我的建议是可以将某种共性的输入要求方式(比如输入数字或者字母等)提出来单独作为一个辅助方法来实现,从而尽量减少重复代码(也就减少了我们的工作量)。另一方面,建议大家提前学习一下正则表达式的使用(反正lab3里面早晚要用),对于健壮性判定代码简化有着很大的帮助。
可视化的部分如果用前面提到的Board思路1实现,这里就很简单,直接把数组元素打出来就可以了。执行情况如下:

四. 总结(建议先看这部分)
- 这个实验里面P1的目的在于让我们养成一个良好的习惯,内容会比较繁琐。而P3是让我们初步进行一个编程的工作,对于不太熟悉没有经验的同学而言可能会比较困难。从时间上来看,三者的比例大约是4:0.5:5.5,而P3中最耗时间的部分在于Game,所以要注意时间的分配。
- 注意总体时间的规划。这门课的一个很大的特点是课程内容会晚于用到相应内容的实验的发布,所以等到老师讲完再写实验,时间上会很紧张。因此,希望大家一方面注意时间的安排,另一方面不要依靠老师讲课,提高自己的预习能力,才能不至于在ddl的前几天过于紧张。
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览92728 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!