软件构造-实验二-仅供参考
姓名 |
|
学号 |
|
班号 |
|
电子邮件 |
|
手机号码 |
|
目录
1 实验目标概述···· 1
2 实验环境配置···· 1
3 实验过程···· 1
3.1 Poetic Walks· 1
3.1.1 Get the code and prepare Git repository· 1
3.1.2 Problem 1: Test Graph <String>· 1
3.1.3 Problem 2: Implement Graph <String>· 1
3.1.3.1 Implement ConcreteEdgesGraph· 2
3.1.3.2 Implement ConcreteVerticesGraph· 2
3.1.4 Problem 3: Implement generic Graph<L>· 2
3.1.4.1 Make the implementations generic· 2
3.1.4.2 Implement Graph.empty()· 2
3.1.5 Problem 4: Poetic walks· 2
3.1.5.1 Test GraphPoet· 2
3.1.5.2 Implement GraphPoet· 2
3.1.5.3 Graph poetry slam·· 2
3.1.6 Before you’re done· 2
3.2 Re-implement the Social Network in Lab1· 2
3.2.1 FriendshipGraph类···· 2
3.2.2 Person类···· 3
3.2.3 客户端main()· 3
3.2.4 测试用例···· 3
3.2.5 提交至Git仓库···· 3
3.3 Playing Chess· 3
3.3.1 ADT设计/实现方案···· 3
3.3.2 主程序ChessGame设计/实现方案···· 3
3.3.3 ADT和主程序的测试方案···· 3
3.4 Multi-Startup Set (MIT) 4
4 实验进度记录···· 4
5 实验过程中遇到的困难与解决途径···· 4
6 实验过程中收获的经验、教训、感想··· 4
6.1 实验过程中收获的经验和教训··· 4
6.2 针对以下方面的感受···· 4
1 实验目标概述
2 本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象 编程(OOP)技术实现 ADT。具体来说:
3 ⚫ 针对给定的应用问题,从问题描述中识别所需的 ADT;
4 ⚫ 设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
5 ⚫ 根据 ADT 的规约设计测试用例;
6 ⚫ ADT 的泛型化;
7 ⚫ 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示 (representation)、表示不变性(rep invariant)、抽象过程(abstraction function)
8 ⚫ 使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表 示泄露(rep exposure);
9 ⚫ 测试 ADT 的实现并评估测试的覆盖度;
10 ⚫ 使用 ADT 及其实现,为应用问题开发程序; ⚫ 在测试代码中,能够写出 testing strategy 并据此设计测试用例。
11 实验环境配置
安装jdk 随意选择目录 只需把默认安装目录 \java 之前的目录修改即可
安装jre→更改→ \java 之前目录和安装 jdk 目录相同即可
安装完JDK后配置环境变量 计算机→属性→高级系统设置→高级→环境变量
系统变量→新建 JAVA_HOME 变量 。
变量值填写jdk的安装目录
系统变量→寻找 Path 变量→编辑
在变量值最后输入 %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
系统变量→新建 CLASSPATH 变量
变量值填写 .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar(注意最前面有一点)
12 实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
12.1 Poetic Walks
图是一个比较基本的数据类型,本次实验通过实现图的数据结构来熟悉软件构造中ADT和OOP技术。
12.1.1 Get the code and prepare Git repository
代码在GitHub中点击按钮就可以下载到本地,也可以使用URL下载到本地,还有GitHub的客户端进行管理。客户端的功能很全面,能实现克隆代码上传等功能。
12.1.2 Problem 1: Test Graph <String>
在Lab1的时候学到了测试类,知道了测试的重要性。
12.1.3 Problem 2: Implement Graph <String>
通过课堂的学习我知道这只是一个ADT,它存在的目的就是提供接口,我们可以通过继承来进行向上转型从而对程序有更好的扩展性。
public static <L> Graph<L> empty()
这个函数的作用就是生成一个空的graph,等着我们实现concreteedgesgrraphD的时候才能实现
public boolean add(L vertex); 增加节点
public int set(L source, L target, int weight); 设置一条边的权重
public boolean remove(L vertex); 移走一个节点
public Set<L> vertices(); 节点的集合
public Map<L, Integer> sources(L target); 获得指向这个节点的节点和权重返回
public Map<L, Integer> targets(L source); 获得所有这个节点指向的节点和权重,然后返回。
12.1.3.1 Implement ConcreteEdgesGraph
这是具体实现
private void checkRep() 负责对保存的数据进行实时的检测,每个方法中都要包含
@Override
public boolean add(String vertex)
重写了add函数,判断这个顶点是否在集合中,如果不在,那么就加入到这个集合中,重写的目的是适应string这个类型的变量
@Override
public int set(String source, String target, int weight)
这个同样是一个重写,生成一个边,然后判断边和顶点和边是否在集合中,进行设定
@Override
public boolean remove(String vertex)
这个重写的目的在于要移除一个顶点和这个顶点所在的边
@Override
public Set<String> vertices()
这个目的是返回顶点的集合
@Override
public Map<String, Integer> sources(String target)
返回所有指向这个顶点的边组成的图
@Override
public Map<String, Integer> targets(String source)
返回所有这个顶点指向的边组成的顶点
public String toString()
进行形象化的输出
class Edge 这个类是最底层的类,就是边,然后有一些对外的接口。
12.1.3.2 Implement ConcreteVerticesGraph
private void checkRep() 这个函数的用处在实验报告中要求的是随时检查变量是否不变。
@Override
public boolean add(String vertex)
重写的目的是添加顶点
@Override
public int set(String source, String target, int weight)
重写的目的是重新设置顶点的权值
@Override
public boolean remove(String vertex)
从集合中移除顶点
@Override
public Set<String> vertices()
返回顶点的集合
@Override
public Map<String, Integer> sources(String target)
返回所有指向这个顶点的图
@Override
public Map<String, Integer> sources(String target)
返回所这个顶点指向的图
class Vertex 这个类就是声明了顶点的权值
发现覆盖率有的很高有的很低。
12.1.4 Problem 3: Implement generic Graph<L>
12.1.4.1 Make the implementations generic
前面我们的范式是string,有局限性,我们希望可以有拓展性。
12.1.4.2 Implement Graph.empty()
向上转型直接返回即可
12.1.5 Problem 4: Poetic walks
应用图,我们应该提供一些语句分解作为资料库,然后将两个单词之间都设为一条边和权重就是出现次数。
12.1.5.1 Test GraphPoet
我们在文件中写资料库,输入句子,要求输入的句子间的单词被添加
12.1.5.2 Implement GraphPoet
其中的方法有checkrep,poem,tostring还有一个构造器。实验的目标很简单,简单来说就是我们的资料库中两个单词之间有连接词,如果我们输入的句子这两个单词是连在一起的,那么输出这两个单词之间应该被填上连接词。
在构造器中我们就将资料库中的单词分开并且转换成小写储存 起来,在poem中拿来与输入的句子进行比较。
12.1.5.3 Graph poetry slam
资料库中的东西可以进行扩展。
12.1.6 Before you’re done
12.2 Re-implement the Social Network in Lab1
让我们通过不同的写法来实现程序的组合功能,程序的可扩展性体现了出来
12.2.1 FriendshipGraph类
运用上面P1写好的类,我将graph稍微改动写成了frendshipgraph类。
12.2.2 Person类
在程序的开始我是先写的person类,因为这个是最基础的,就是将LAB1实现的功能再写一遍。
12.2.3 客户端main()
FriendShipGraph graph = new FriendShipGraph();
Person rachel = new Person("Rachel");
Person ross = new Person("Ross");
Person ben = new Person("Ben");
Person kramer = new Person("Kramer");
graph.addVertex(rachel);
graph.addVertex(ross);
graph.addVertex(ben);
graph.addVertex(kramer);
graph.addEdge(rachel, ross);
graph.addEdge(ross, rachel);
graph.addEdge(ross, ben);
graph.addEdge(ben, ross);
System.out.println(graph.getDistance(rachel,ross));
//should print 1
System.out.println(graph.getDistance(rachel,ben));
//should print 2
System.out.println(graph.getDistance(rachel,rachel));
//should print 0
System.out.println(graph.getDistance(rachel,kramer));
//should print -1
Main函数就是上次的函数
12.2.4 测试用例
测试用例就是在上次提供的main函数上加加减减,覆盖率高一些。
12.2.5 提交至Git仓库
12.3 Playing Chess
12.3.1 ADT设计/实现方案
一共写了action board chessaction chesspiece game goaction gopiece mychessandgogame piece player position 这几个类,其中position类是最低层的,其次是piece有对象包括了positon ,player中实现了玩家添加和棋子之间的关系,有piece的集合,有相当一部分是接口。就是将playername gamehistory都提供一个接口。Board会根据是要国际象棋还是围棋来创建二维数组。Chessaction和goaction都是继承子action分别对象棋和围棋做出了加子,移动棋子或去掉棋子的操作
12.3.2 主程序MyChessAndGoGame设计/实现方案
System.out.print("input: \n1.Chess\n2.Go\n");
Scanner input=new Scanner(System.in);
int temp1=input.nextInt();
Board board=new Board(temp1);//初始化棋盘,输入两个对弈者
ChessAction myaction=new ChessAction();
GoAction myAction2=new GoAction();
/*System.out.println("input x");
int x=input.nextInt();
System.out.println("input y");
int y=input.nextInt();
Position pos=new Position(x, y);*/
if(temp1==1)
{
String endString="start";
while(!endString.equals("end"))
{
System.out.println("是否要继续输入,结束输入end");
System.out.println("input :");
endString=input.next();
System.out.println("input x");
int x=input.nextInt();
System.out.println("input y");
int y=input.nextInt();
Position pos=new Position(x, y);
System.out.println(" --------------------------");
System.out.println("你想进行什么操作?");
System.out.println("3 .移动棋盘某个坐标的子");
System.out.println("4 .吃掉棋盘的某个坐标的子");
System.out.println("input");
int choose=input.nextInt();
if(choose==3)
{
System.out.println("input target x");
int a=input.nextInt();
System.out.println("input target y");
int b=input.nextInt();
Position targetPosition=new Position(a, b);
myaction.chessmove(pos, targetPosition);
}
if(choose==4)
{
myaction.chesseat(pos);
}
/*else
{
System.out.println("国际象棋不能移动没有棋子的地方");
System.exit(0);
}*/
}
System.out.println("谢谢使用");
System.exit(0);
}
if(temp1==2)
{
System.out.println("是否要继续输入,结束输入end");
System.out.println("input :");
String endString=input.next();
while(!endString.equals("end"))
{
System.out.println("input x");
int x=input.nextInt();
System.out.println("input y");
int y=input.nextInt();
Position pos=new Position(x, y);
System.out.println("3 下棋");
System.out.println("4 吃子");
int choose=input.nextInt();
if(choose==3)
{
System.out.println("输入白子还是黑子");
String test=input.next();
myAction2.goadd(pos,test);
/*else
{
System.out.println("不能在有棋的地方下棋");
return;
}*/
}
if(choose==4)
{
myAction2.goeat(pos);
}
/*else {
System.out.println("不能在没有棋的地方吃子");
System.exit(0);
}*/
}
}
我的这个类用来设计实现UI交互,在每次输入时都会判断是否输入end停止。
12.3.3 ADT和主程序的测试方案
针对各个ADT写出对应于其中方法的测试样例,比如返回布尔值的方法就输入一个值,看看是否返回了自己预想的值。还有针对类中的变量都要测试是否在范围内。
比如要测试player类中的gamehistory不能是空。
12.4 Multi-Startup Set (MIT)
请自行设计目录结构。
注意:该任务为选做,不评判,不计分。
13 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 |
时间段 |
计划任务 |
实际完成情况 |
2019.4.1 |
14:37-15:30 |
P1 |
写完vertex相关的类 |
2019.4.3 |
17:20-20:57 |
P1 |
写完 |
2019.4.4 |
略 |
P2 |
花了一天时间写完较为简单的P2 |
20194.5 |
17:01-21:36 |
P3 |
P3比较复杂,花了大量时间来完善接口和参数的调整,方法的重载。花费时间较长。未完成 |
2019.4.6 |
All day |
P3 |
写完player piece position |
2019.4.7 |
All day |
P3 |
P3写完 |
14 实验过程中遇到的困难与解决途径
遇到的难点 |
解决途径 |
构造器写成了方法
|
将返回值去掉 |
参数与想要的方法不匹配
|
进行参数向上转型并且进行方法的重载。 |
总是进不去.size循环
|
发现没有初始化 |
接口对应不上 |
重新写了一个接口 |
15 实验过程中收获的经验、教训、感想
15.1 实验过程中收获的经验和教训
收获了对ADT和继承的更深入的理解,下次我将更为仔细的构造程序的底层,力争方法最简单,靠组合实现功能。
这次类写的太复杂,写到后面都忘记关系了。
15.2 针对以下方面的感受
(1) 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
(2) 使用泛型和不使用泛型的编程,对你来说有何差异?
(3) 在给出ADT的规约后就开始编写测试用例,优势是什么?你是否能够适应这种测试方式?
(4) P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
(5) P3要求你从0开始设计ADT并使用它们完成一个具体应用,你是否已适应从具体应用场景到ADT的“抽象映射”?相比起P1给出了ADT非常明确的rep和方法、ADT之间的逻辑关系,P3要求你自主设计这些内容,你的感受如何?
(6) 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后编程中坚持这么做?
(7) 关于本实验的工作量、难度、deadline。
(8) 《软件构造》课程进展到目前,你对该课程有何体会和建议?
面向对象编程我感到自己不是在写程序而是在搭建程序。但是不如面向过程的直接。我个人感觉还好,这种思想慢慢的被我接受。复用的函数给程序带来了可扩展性,个人感觉P3还是不利于新手入手,理解并融会贯通需要一段的时间。