软件构造Lab2-实验报告
实验目标概述
本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现ADT。具体来说:
⚫ 针对给定的应用问题,从问题描述中识别所需的ADT;
⚫ 设计ADT规约(pre-condition、post-condition)并评估规约的质量;
⚫ 根据ADT的规约设计测试用例;
⚫ ADT的泛型化;
⚫ 根据规约设计ADT的多种不同的实现;针对每种实现,设计其表示(representation)、表示不变性(rep invariant)、抽象过程(abstraction function)
⚫ 使用OOP实现ADT,并判定表示不变性是否违反、各实现是否存在表示泄露(rep exposure);
⚫ 测试ADT的实现并评估测试的覆盖度;
⚫ 使用ADT及其实现,为应用问题开发程序;
⚫ 在测试代码中,能够写出testing strategy并据此设计测试用例。
2.实验环境配置
延续Lab1中的实验环境,除此之外,本次实验在 Eclipse IDE中安装配置 EclEmma(一个用于统计JUnit测试用例的代码覆盖度测试用例的代码覆盖度plugin)
在这里给出你的GitHub Lab2仓库的URL地址(Lab2-学号):
https://github.com/ComputerScienceHIT/Lab2-1173710111
3.实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)
3.1Poetic Walks
- 对给定的接口Graph<L>,完成他的两个实例类ConcretVerticesGraph和ConcreateEgdesGraph:
(1)首先分别构造两个实例类的rep类Vertex和Edge;
(2)用String作为L的特例,分别在两个实例类中实现Graph接口中的各个方法,写对应的test文件以确保其正确性;
(3)将所有String变量替换为泛型L。
2. 对给定的文本文件,按照给定规则建立有向图,调用Graph<L>作为存储语料库的数据结构,利用该结构,对输入的字符串进行扩充。
3.1.1Get the code and prepare Git repository
请阅读 http://web.mit.edu/6.031/www/sp17/psets/ps2/,遵循该页面内的要求完 ,遵循该页面内的要求完成编程任务。
⚫ 在 Get the code步骤中你无法连接MIT的 Athena服务器,请从以下地址获取初始代码 :
https://github.com/rainywang/Spring2019_HITCS_SC_Lab2/tree/master/P1
⚫ 在作业描述中若遇到 “commit and push”的要求 ,请将你的代码 push到你的 GitHub Lab2仓库中 。
⚫ 其他步骤请遵循 MIT。
自 https://github.com/rainywang/Spring2019_HITCS_SC_Lab2/tree/master/P1 获得实验代码。
git初始化:
Git init
Git remote add origin git@github.com:ComputerScienceHIT/Lab2-1173710111.git
Git pull origin master
Git add .
Git commit -m “xxx”
Git push origin master
3.1.2Problem 1: Test Graph <String>
静态 Graph.empty()方法的测试策略和测试都在GraphStaticTest.java,为了运行这个测试,首先要修改Graph<L> empty()为:
public static String Graph empty() {
Graph graph = new ConcreteEdgesGraph();
return graph;
}
这里以ConcreteEdgesGraph作为Graph默认的实例类,也可以用ConcreteVerticesGraph,二者是等价的
3.1.3 Problem 2: Implement Graph <String>
3.1.3.1Implement ConcreteEdgesGraph
(1)实现 Edge<String>类
- rep:
private int weight; |
边的权值 |
private String sourceVertex; |
边的起点 |
private String targetVertex; |
边的终点 |
- constructor:
public Edge(L source,L target,int w){ this.weight=w; this.sourceVertex=source; this.targetVertex=target; } |
- function:
getter |
三个域的getter,因为规定Edge<L>是Immutable的所以没有setter。 |
public void checkRep() { assert sourceVertex!=null; assert targetVertex!=null; assert weight>=0; } |
每个getter返回前检查该边的起点、终点、权值是否有空的。 |
@Override public String toString() |
以字符串的形式直观地展示该边的起点、终点和边权,格式为: source->target [weight] |
(2)实现ConcreteEdgesGraph类
- rep:
private final Set<String> vertices |
保存图中的所有顶点 |
private final List<Edge<String>> edges |
顺序存储图中的所有边 |
- function:
每个function返回前检查rep不变 private void checkRep() { for(int i=0;i<edges.size();i++) { assert vertices.contains(edges.get(i).getSourceVertex()):"an edge with illegal vertex"; assert vertices.contains(edges.get(i).getTargetVertex()):"an edge with illegal vertex"; for(int j=i+1;j<edges.size();j++) { assert !(edges.get(i).getSourceVertex().equals(edges.get(j).getSourceVertex())&&edges.get(i).getTargetVertex().equals(edges.get(j).getTargetVertex())) :"duplicate edges"; } assert edges.get(i)!=null; } for(L s:vertices) { assert s!=null; } } |
|
@Override public boolean add(String vertex) |
(1)点存在:返回false (2)不存在:调用list.add向vertices中添加该点 |
@Override public int set(String source, String target, int weight) |
遍历edges: (1)如果已经存在从source到target的一条边,记下这条边的权值,创建一条新的边(参数为source,target,weight),调用Collections.replaceAll替换掉edges中的旧边,返回旧边权; (2)如果不存在,新建一条边(参数为source,target,weight),将其加入edges,返回0。 |
@Override public boolean remove(L vertex) |
调用set.contains判断图中是否存在该顶点:(1)如果不存在,返回false;
|
@Override public Set<L> vertices() |
copy一份vertices到新Set中,返回这个Set |
@Override public Map<L, Integer> sources(L target) |
建立一个空Map,遍历edges,找出以target为终点的边edge; weight=0时表示该边不存在,跳过; 将edge.source->edge.weight加入Map,遍历结束后返回这个Map |
@Override public Map<L, Integer> targets(L source) |
建立一个空Map,遍历edges,找出以source为起点的边edge; weight=0时表示该边不存在,跳过; 将edge.source->edge.weight加入Map,遍历结束后返回这个Map |
@Override public String toString() |
将所有边的toString连接在一起 |
(3)编写测试 GraphInstanceTest.java
public abstract Graph<String> emptyInstance(); |
抽象方法,创建新的空Graph<String> |
@Test public void testAdd() |
分别对input未存在、已存在的情况进行测试,检查其rep是否与期待相符 |
@Test public void testRemove() |
|
@Test public void testSources() |
1. 参数vertex的source域为空; 2. 参数vertex的target域为空; 3. 对set方法传入weight=0的情况进行测试 方法调用后,对相关参数(边)调用source方法或target方法的返回值进行检查 |
@Test public void testTargets() |
|
@Test public void testSet() |
方法调用后,对相关参数(边)调用source方法或target方法的返回值进行检查 |
@Test public void testVertices() |
调用add和remove方法,然后对vertices方法的返回值进行检查 |
@Test public void testALL() |
创建一个较复杂的有向图,调用到每个方法,对rep进行检查 |
(4)编写测试ConcreteEdgesGraphTest.java
Graph中定义的接口 |
GraphInstanceTest.java中实现,不必重复 |
创建一个新的空图 ConcreteEdgesGraph @Override public Graph<String> emptyInstance() { return new ConcreteEdgesGraph<String>(); } |
|
public void testToString() |
调用Graph中的方法构建一张有向图,测试toString的返回值是否能正确规范地表示这张图。 |
public void testVertex() |
主要测试Edge中的toString方法,测试其返回值能否正确规范地表示这条边的信息。 |
3.1.3.2Implement ConcreteVerticesGraph
(1)实现Vertex<L>类
- rep:
private L name; |
当前节点的名字(标签) |
private Map<L, Integer>source; |
存储所有以当前点为终点的边,key表示边的起点,value表示边权 |
private Map<L, Integer>target; |
存储所有以当前点为起点的边,key表示边的终点,value表示边权 |
- constructor:
public Vertex(L name){ this.name=name; } |
- function:
setter |
source,target的setter |
getter |
name,source,target的getter |
public void checkRep() { assert source!=null; assert target!=null; assert name!=null; } |
每个getter返回前检查当前点的rep是否非空。 |
@Override public String toString() |
以字符串的形式直观地展示有哪些边以当前点为终点\起点,格式为: {[source,weight], ...}->{[target,weight], ...} |
(2)实现ConcreteVertices类
- rep:
private final List<Vertex<L>> vertices; |
存储图的顶点 |
- function:
每个function返回前检查rep不变 private void checkRep(){ for(int i=0;i<vertices.size();i++) { for(int j=i+1;j<vertices.size();j++) { assert !(vertices.get(i).getName().equals(vertices.get(j).getName())) :"duplicate vertices"; } assert vertices.get(i)!=null; } for(Vertex<L> s:vertices) { assert s!=null; } } |
|
@Override public boolean add(L vertex) |
遍历vertices:
|
@Override public int set(L source, L target, int weight) |
(1)先调用add加入两个顶点; (2)首先遍历vertices,找到标签与source相等的顶点vertex,然后遍历该顶点的vertex.target,找到标签与target相等的顶点,获取对应的边权,然后更新边权为weight,返回旧边权; (3)在(2)中如果没有找到标签与target相等的顶点,则遍历vertices进行寻找,如果找到了,建立target->weight的映射并加入vertex.target,返回0。 |
@Override public boolean remove(L vertex) |
|
@Override public Set<L> vertices() |
copy一份vertices到新Set中,返回这个Set |
@Override public Map<L, Integer> sources(L target) |
建立一个空Map,遍历vertices,如果某个vertex其vertex.target不为空并且包含了target,就建立vertex->vertex.weight的映射并加入Map; weight=0时表示该边不存在,跳过; 遍历结束后返回Map |
@Override public Map<L, Integer> targets(L source) |
建立一个空Map,遍历vertices,如果某个vertex其vertex.source不为空并且包含了source,就建立vertex->vertex.weight的映射并加入Map; weight=0时表示该边不存在,跳过; 遍历结束后返回Map |
@Override public String toString() |
将所有点的toString连接在一起 |
(3)编写测试ConcreteVerticesGraphTest.java
Graph中定义的接口 |
GraphInstanceTest.java中实现,不必重复 |
创建一个新的空图ConcreteVerticesGraph @Override public Graph<String> emptyInstance() { return new ConcreteVerticesGraph<String>(); } |
|
public void testToString() |
调用Graph中的方法构建一张有向图,测试toString的返回值是否能正确规范地表示这张图。 |
public void testVertex() |
主要测试Vertex中的toString方法,测试其返回值能否正确规范地表示这个顶点的信息。 |
3.1.4 Problem 3: Implement generic Graph<L>
3.1.4.1 Make the implementations generic
将两个实例类中的所有String类的参数替换为泛型<L>的参数(声明、函数参数、返回值、rep)
3.1.4.2 Implement Graph.empty()
修改Graph.empty为:
public static <L> Graph<L> empty() {
Graph<L> graph = new ConcreteEdgesGraph<L>();
return graph;
}
这里以ConcreteEdgesGraph作为Graph<L>默认的实例类,也可以用ConcreteVerticesGraph,二者是等价的
3.1.5 Problem 4: Poetic walks
3.1.5.1 Test GraphPoet
语料库文件配置为:new File("src/P1/poet/mugar-omni-theater.txt") |
文本为:This is a test of the Mugar Omni Theater sound system. |
@Test public void testGraphPoet() throws IOException { final GraphPoet nimoy = new GraphPoet(new File("src/P1/poet/mugar-omni-theater.txt")); final String input = "Test the system."; assertEquals("Test of the system.",nimoy.poem(input)); } |
3.1.5.2Implement GraphPoet
rep:
private Graph<String> corpusGraph = Graph.empty(); |
用于存储文本文件读入后按规则建立的有向图 |
constructor:
public GraphPoet(File corpus) throws IOException |
根据参数(文件),判断能否正常读入(否则抛出异常),并且调用poemGraph(corpus)方法创建好语料库(有向图) |
function:
public void checkRep() { assert !corpusGraph.vertices().isEmpty(); } |
各方法返回前检查rep不为空 |
public Graph<String> poemGraph(File corpus) |
根据参数(文件)创建语料库,规则为:相邻两个单词直接建立一条权值为1的有向边,有重复边时权值+1。 首先将文件以字符串形式读入,按“ ”(空格)划分为各个单词,不区分大小写,然后线性扫描,调用corpusGraph.set()建边 |
public String poem(String input) |
对input进行扩展,规则是:如果相邻两个单词a和b,在语料库中存在一个单词c使a->c和c->b都存在,则将c插入a和b之间;如果存在多个满足条件的c,取边权a->c较大的。 首先将input按“ ”(空格)划分为各个单词,不区分大小写,线性扫描,调用corpusGraph.target()和source()找出所有可能的“桥”(上面说到的c),然后找到边权最大的一个,插入。最后返回扩展好的字符串。 |
public String toString() |
return corpusGraph.toString(); |
3.1.5.3 Graph poetry slam
更新语料库文件中的字符串和输入的字符串input
3.1.6 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库:
Git add .
Git commit -m “xxx”
Git push origin master
在这里给出你的项目的目录结构树状示意图。
项目名称:Lab2-1173710111 |
3.2 Re-implement the Social Network in Lab1
继承P1中ConcreteEdgesGraph<Person>或者ConcreteVerticesGraph<Person>类 实现FriendshipGraph,通过基本操作实现FriendshipGraph中addVertex,addEdge和getDistance三个接口,要求不能修改父类rep。
3.2.1 FriendshipGraph类
public void addVertex(Person person) |
遍历父类的vertices(),如果存在一个元素的name域与Person的name域相等,证明这个点已经存在,输出提示,否则调用父类的add(person)将该点加入 |
public void addEdge(Person p1, Person p2) |
先调用父类的set(p1,p2,1),如果返回值为0证明这两个点之间不存在边,否则证明这两个点之间已经有边存在,输出提示 |
public int getDistance(Person p1, Person p2) |
使用BFS算法求p1与p2之间的最短距离,BFS需要遍历邻居节点时调用父类接口的targets(p1)就可以获得p1的所有邻居节点。 |
public Set<Person> persons() |
copy一份vertices,存储图中所有的顶点,即关系网络中的所有人。 |
public static void main(String Args[]) |
与Lab1相同 |
3.2.2Person类
由于继承了ConcreteVerticesGraph<Person>,所以可以调用父类的rep和function,因此Person类就不需要过多的修饰。 |
public class Person { private String name; public boolean vis=false; public Person(String name) { this.name=name; } public String getName() { return this.name; } } |
3.2.3 客户端main()
Lab1实验手册给出。
3.2.4 测试用例
public void addVertexTest() |
测试重名点 |
public void addEdgeTest() |
测试重边 |
public void getdistance() |
测试非连通点和两个相同点 |
3.2.5提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
Git add .
Git commit -m “xxx”
Git push origin master
在这里给出你的项目的目录结构树状示意图。
3.3 Playing Chess
3.3.1ADT设计/实现方案
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。描述你设计的各ADT间的关系。
- Position(mutable)
坐标类:定义一个点(棋子)的坐标位置,并且提供判重方法
rep:
private int x; |
横纵坐标 |
private int x; |
constructor:
public Position(int x,int y) { this.x=x; this.y=y; } |
function:
public void setPosition(int x,int y) |
修改横纵坐标 |
public int getPositionX() |
获取横纵坐标 |
public int getPositionY() |
|
public boolean equals(Position that) |
判断两个Position相等 |
2. Piece(mutable)
棋子类:定义一个棋子,包括棋子所属的游戏类型(围棋/象棋),棋子类型(黑白/将相车卒等),棋子状态(未放置,待放置,已移除);
rep:
//player1,player2 final private String belong; |
棋子属于哪个玩家,不可变 |
final private String type; |
棋子的类型,不可变 围棋有“black”“white”两种,国际象棋有很多种(兵”P”、王”K”、后”Q”等) |
//0-unplaced,1-placed,2-removed. private int pieceState; |
棋子的状态,可变 0-unplaced,1-placed,2-removed |
private Position piecePosition; |
棋子在棋盘上的位置,可变 |
constructor:
public Piece(String belong,String type) { this.belong=belong; this.type=type; } |
function:
setter |
state和piecePositon有对应的getter |
getter |
四个域都有getter,piecePosition的getter有两个,分别返回横纵坐标 |
3. Player(mutable)
玩家类,包括玩家的基本信息,以及玩家所拥有的棋子信息。
rep:
final private String name; |
玩家的名字,不可更改 |
private Set<Piece> pieces=new HashSet<Piece>(); |
存储玩家拥有的所有棋子 |
private List<String> historyList=new LinkedList<String>(); |
顺序保存玩家在一局游戏里的走棋历史 |
constructor:
public Player(String name) { this.name=name; } |
function:
getter |
三个域的getter |
public boolean addPiece(Piece newPiece) |
添加一枚棋子,已经拥有返回false,否则添加并返回true |
public boolean removePiece(Piece removePiece) |
移除一枚棋子,不存在返回false,否则移除并返回true |
public int getNumberOfPieces() |
获取棋盘上属于自己的棋子数,即统计自己的棋子里pieceState为1的棋子数 |
public void addStep(String step) |
向historyList域中添加一个走棋步骤 |
4. Board(mutable)
定义一个名为“棋盘”的类,在这个类里定义了棋盘的规格,并生成一个空的棋盘。 规格:由一个正整数n决定的一个n*n的正方形
rep:
final private int type; |
棋盘的类型: 0 means the pieces are placed in blanks[0,n-1], 1 means the pieces are placed on points[0,n]. |
final private int size; |
棋盘的大小,size*size |
private Piece[][] pieces; |
存储棋盘的某个位置上放置的棋子 |
private boolean[][] placed; |
判断棋盘上的某个位置是否放置了棋子 |
constructor:
public Board(int type,int size) { this.type=type; this.size=size; size+=type; //根据size的大小初始化两个二维数组 pieces=new Piece[size][size]; placed=new boolean[size][size]; //初始化,棋盘最初是空的 for (int i=0;i<size;i++) { for (int j=0;j<size;j++) placed[i][j]=false; } } |
function:
getter |
type和size的getter |
public Piece getPiece(int x,int y) |
给出一对横纵坐标,获取该位置上的棋子,如果没有,返回的为空,因此需要在客户端进行判断 |
public boolean setPiece(Piece p,int x,int y) |
在位置(x,y)上放置piece |
public void setPlaced(int x,int y) |
将(x,y)对应的Placed状态置为true/false |
public void setNotPlaced(int x,int y) |
|
public boolean getPlaced(int x,int y) |
获取(x,y)对应位置是否放置了棋子 |
5. Action(interface)
提供棋类游戏各种行为的接口
public static Action empty(String gameType) |
根据输入的字符串判断以哪一个实例类作为默认的实现 |
public Player createPlayer1(String playerName1); |
创建两个玩家 |
public Player createPlayer2(String playerName2); |
|
public Board board(); |
创建一个新的空棋盘 |
public void initialize() |
* 初始化,包括: * 1。设置相关参数 * 2。生成一个棋盘并初始化 * 3。生成一组棋子 * 4。给定两个名称,初始化两个Player对象 * 5。将棋子分配给两个Player对象 |
public boolean placePiece(Player player,Piece piece,int x,int y); |
* 给定“棋手、一颗棋子、指定位置的横坐标、指定位置的纵坐标”作为输入参数,将该棋手的该颗棋子放置在棋盘上 *异常情况,例如:该棋子并非属于该棋手、指定的位置超出棋盘的范围、指定位置已有棋子、所指定的棋子已经在棋盘上等,返回false * 否则,将棋子落在指定位置,返回true |
public boolean movePiece(Player player,int sx,int sy,int tx,int ty); |
* 给定“棋手、初始位置和目的位置的横纵坐标”,将处于初始位置的棋子移动到目的位置。 * 需要考虑处理各种异常情况,例如: * 指定的位置超出棋盘的范围、目的地已有其他棋子、初始位置尚无可移动的棋子、两个位置相同、初始位置的棋子并非该棋手所有等,返回false * 否则,将处于初始位置的棋子移动到目的位置,返回true。 |
public boolean removePiece(Player player,int x,int y); |
* 给定“棋手、一个位置的横纵坐标”,将该位置上的对手棋子移除。 * 需要考虑处理异常情况,例如:该位置超出棋盘的范围、该位置无棋子可提、所提棋子不是对方棋子、等,返回false。 * 否则,移除棋子并返回true。 |
public boolean eatPiece(Player player,int sx,int sy,int tx,int ty); |
* 给定“棋手、两个位置横纵坐标”,将第一个位置上的棋子移动至第二个位置,第二个位置上原有的对手棋子从棋盘上移除。 * 需要处理异常情况,例如: * 指定的位置超出棋盘的范围、第一个位置上无棋子、第二个位置上无棋子、两个位置相同、第一个位置上的棋子不是自己的棋子、第二个位置上的棋子不是对方棋子、等. * 返回false。 * 否则,返回true。 |
有两个实例类:ChessAction和GoAction
(1)ChessAction
因为国际象棋中没有落子和提子的行为,所以客户端并不允许调用placePiece和removePiece,这两个接口在实现时返回值永远为false。
rep:
final protected static int boardsize=8; |
棋盘大小,国际象棋棋盘大小为8*8 |
final protected static int piecesize=32; |
棋子总个数,国际象棋初始时棋子最多共32个 |
protected Player player1; |
调用createPlayer1和createPlayer2创建两个Player |
protected Player player2; |
|
protected Board chessBoard=new Board(0, boardsize); |
新建一个规格为8*8的棋盘 |
(2)GoAction
因为围棋中没有移子吃子的行为,所以客户端并不允许调用movePiece和eatPiece,这两个接口在实现时返回值永远为false。
rep:
final protected static int boardsize=18; |
棋盘大小,围棋棋盘大小为19*19 |
final protected static int piecesize=361; |
棋子总个数,围棋棋盘上最多有361个位置可以放置棋子 |
protected Player player1; |
调用createPlayer1和createPlayer2创建两个Player |
protected Player player2; |
|
protected Board chessBoard=new Board(0, boardsize); |
新建一个规格为19*19的棋盘 |
6. Game(immutable)
这是一个游戏类,用来创建一个棋盘游戏,并提供所有可能使用的功能。
rep:
private Action gameAction; |
创建一个新的棋类Action |
private Player player1; |
两个玩家 |
private Player player2; |
|
private Board board; |
棋盘 |
constructor:
给定游戏类型,两个玩家的名字,创建一个新的Game |
public Game(String game,String playerName1,String playerName2) { gameAction=Action.empty(game); player1=gameAction.createPlayer1(playerName1); player2=gameAction.createPlayer2(playerName2); gameAction.initialize(); board=gameAction.board(); } |
function:
public int getBoardSize() |
1.get the size of board; |
public String getPlayer1() |
2.get the names of two players; |
public String getPlayer2() |
|
public boolean placePiece(String playerName,int x,int y) |
3.achieve some kinds of behavior of board games, such as falling, lifting, moving and eating pieces. *输入非法时输出提示并返回false,否则返回true |
public boolean movePiece(String playerName,int sx,int sy,int tx,int ty) |
|
public boolean removePiece(String playerName,int x,int y) |
|
public boolean eatPiece(String playerName,int sx,int sy,int tx,int ty) |
|
public boolean checkBoardPlaced(int x,int y) |
4.given a position,check the state of this position on board,whether is occupied or not,if occupied,occupied by whose which pieces. |
public int getNuberOfPiece(String playerName) |
5.calculate the numbers of the two players' pieces. |
public String getHistorySteps(String playerName) |
6.given a player’s name ,output the history of this game in this competition. |
3.3.2 主程序MyChessAndGoGame设计/实现方案
辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。
(1)主程序入口
public static void main(String[] args) { new MyChessAndGoGame().gameMain(); } |
(2)主函数
public void gameMain() { Scanner in=new Scanner(System.in); String gameType; while(true) { System.out.println("******************"); System.out.println("*欢迎来到不正经棋牌室!*"); System.out.println("******************"); System.out.println("请选择想要创建的棋类游戏:"); System.out.println("1. 国际象棋"+"\t"+"2. 围棋"); int p=in.nextInt(); if (p==1) { gameType=new String("chess"); in.nextLine(); break; }else { if (p==2) { gameType=new String("go"); in.nextLine(); break; }else { continue; } } } System.out.println("请输入玩家1的名字:"); String playerName1; playerName1=in.nextLine(); System.out.println("请输入玩家2的名字:"); String playerName2; playerName2=in.nextLine(); //调用Game类创建了一盘新的棋类游戏 Game game=new Game(gameType, playerName1, playerName2); if (gameType.contentEquals("chess")) { //根据选择的棋类游戏,分别调用函数继续 startChessGame(game); }else { startGoGame(game); } in.close(); } |
(3)以围棋为例,进行游戏的函数:
public void startGoGame(Game game) { while(true) { //玩家1先手: System.out.println("棋盘的大小为:"+game.getBoardSize()+"*"+game.getBoardSize()); printGoMenu(); System.out.print("玩家 "+game.getPlayer1()+" 请输入选项:"); Scanner in=new Scanner(System.in); String choice=in.nextLine(); //根据选择、进行行动的玩家名,继续下面的行为: chooseOfGo(game, choice,game.getPlayer1(),in); System.out.println(); if (choice.equals("end")) in.close(); //玩家2后手: System.out.println("棋盘的大小为:"+game.getBoardSize()+"*"+game.getBoardSize()); printGoMenu(); System.out.print("玩家 "+game.getPlayer2()+" 请输入选项:"); String choice2=in.nextLine(); chooseOfGo(game, choice2,game.getPlayer2(),in); System.out.println(); if (choice.equals("end")) in.close(); } } |
(4)根据不同选项进行不同操作的函数:
public void chooseOfGo(Game game,String choice,String playerName,Scanner in) { int x=0,y=0; switch (choice) { case "1": do{ System.out.println("请输入落子位置的横纵坐标(x,y):"); try { String xyString=in.nextLine(); String[] xy=xyString.split(","); x=Integer.valueOf(xy[0]); y=Integer.valueOf(xy[1]); }catch (Exception e) { // TODO: handle exception e.getStackTrace(); } }while (!game.placePiece(playerName, x, y)); break; case "2": do{ System.out.println("请输入提子位置的横纵坐标(x,y):"); try { String xyString=in.nextLine(); String[] xy=xyString.split(","); x=Integer.valueOf(xy[0]); y=Integer.valueOf(xy[1]); }catch (Exception e) { // TODO: handle exception e.getStackTrace(); } }while(!game.removePiece(playerName, x, y)); break; case "3": System.out.println("请输入想要查询的位置的横纵坐标(x y):"); String xyString=in.nextLine(); String[] xy=xyString.split(","); x=Integer.valueOf(xy[0]); y=Integer.valueOf(xy[1]); game.checkBoardPlaced(x, y); System.out.println("棋盘的大小为:"+game.getBoardSize()+"*"+game.getBoardSize()); printGoMenu(); System.out.print("玩家 "+playerName+" 请输入选项:"); choice=in.nextLine(); chooseOfGo(game,choice,playerName,in); break; case "4": System.out.println("玩家 "+game.getPlayer1()+" 在棋盘上的棋子总数为:"+game.getNuberOfPiece(game.getPlayer1())); System.out.println("玩家 "+game.getPlayer2()+" 在棋盘上的棋子总数为:"+game.getNuberOfPiece(game.getPlayer2())); System.out.println("棋盘的大小为:"+game.getBoardSize()+"*"+game.getBoardSize()); printGoMenu(); System.out.print("玩家 "+playerName+" 请输入选项:"); choice=in.nextLine(); chooseOfGo(game,choice,playerName,in); break; case "5": extracted(); break; case "end": System.out.println("玩家"+game.getPlayer1()+"的走棋历史:"); System.out.println(game.getHistorySteps(game.getPlayer1())); System.out.println("玩家"+game.getPlayer2()+"的走棋历史:"); System.out.println(game.getHistorySteps(game.getPlayer2())); System.out.print("欢迎下次再来!"); System.exit(0); break; default: System.out.println("输入错误,请重新输入:"); choice=in.nextLine(); chooseOfGo(game,choice,playerName,in); } } private void extracted() { return; } |
(5)以国际象棋为例,程序运行截图如下:
3.3.3ADT和主程序的测试方案
介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
(1) ActionInstanceTest
public Action emptyInstance(); |
/** * Overridden by implementation-specific test classes. * * @return a new empty Action of the particular implementation being tested */ |
public void testCreatePlayers(); |
/** * test whether the action create two right players. */ |
public void testInitialize(); |
/** * Overridden by implementation-specific test classes. * Check every position on the board, and pieces, for proper initialization. */ |
public void testPlacePiece(); |
/** * Overridden by ChessActionTest. * place a piece on a specific position,and check whether the state is right. * test every illegal input. * test some legal input,and check the state of player,board,and piece after placePiece. */ |
public void testMovePiece(); |
/** * Overridden by ChessActionTest. * move a piece from a specific position to another,and check whether the state is right. * test every illegal input. * test some legal input,and check the state of player,board,and piece after movePiece. */ |
public void testRemovePiece(); |
/** * Overridden by ChessActionTest. * remove a piece from a specific position,and check whether the state is right. * test every illegal input. * test some legal input,and check the state of player,board,and piece after removePiece. */ |
public void testEatPiece(); |
/** * Overridden by ChessActionTest. * use a piece from a specific position to eat another,and check whether the state is right. * test every illegal input. * test some legal input,and check the state of player,board,and piece after eatPiece. */ |
(2) public class ChessActionTest implements ActionInstanceTest
(3) public class GoActionTest implements ActionInstanceTest
(4)GameTest
rep:
private Game chessGame; |
private Game goGame; |
function:
@Before public void initialize(){ chessGame=new Game("chess","ChessPlayer1", "ChessPlayer2"); goGame=new Game("go","GoPlayer1", "GoPlayer2"); } |
|
@Test public void testPlacePiece() |
/** * test some illegal input. * test some legal input,and check the state of player,board after placePiece,and the return value. */ |
@Test public void testMovePiece() |
/** * test some illegal input. * test some legal input,and check the state of player,board after movePiece,and the return value. */ |
@Test public void testRemovePiece() |
/** * test some illegal input. * test some legal input,and check the state of player,board after removePiece,and the return value. */ |
@Test public void testEatPiece() |
/** * test some illegal input. * test some legal input,and check the state of player,board after eatPiece,and the return value. */ |
@Test public void testGetGistorySteps_Go() |
/** * make some steps,and then test the output of getHistorySteps */ |
@Test public void testGetGistorySteps_Chess() |
(5) ToolClassTest
/**
* test some methods of Player,Board,Piece,Position
*/
rep:
Board board; |
Player player; |
List<Piece> pieces=new ArrayList<Piece>(); |
function:
@Before public void initialize() { board=new Board(0, 5); player=new Player("player"); List<Piece> pieces=new ArrayList<Piece>(); for (int i=0;i<10;i++) { Piece piece=new Piece("player", "1"); pieces.add(piece); } } |
|
@Test public void testPlayer() |
test player.getHistory() |
@Test public void testPosition() |
test position.equals() |
软件构造Lab2-实验报告相关推荐
- 软件构造lab2 - 实验报告
软件构造lab2 - 实验报告 1.实验目标概述 2.环境配置 3.实验过程 3.1Poetic Walks 3.1.1Get the code and prepare Git repository ...
- 哈工大2020软件构造Lab2实验报告
本项目于3.17日实验课验收,请放心参考 参考时文中有给出一些建议,请查看 基本更新完成 2020春计算机学院<软件构造>课程Lab2实验报告 Software Construction ...
- HIT 软件构造 lab2实验报告
2020年春季学期 计算机学院<软件构造>课程 Lab 2实验报告 学号 1180300223 班号 1803002 目录 1 实验目标概述 1 2 实验环境配置 1 3 实验过程 1 3 ...
- 软件构造 Lab2 实验报告
2021年春季学期 计算学部<软件构造>课程 Lab 2实验报告 姓名 王雨宁 学号 1190201118 班号 1903006 电子邮件 1756840811@qq.com 手机号码 1 ...
- 哈工大软件构造lab2实验报告
1 实验目标概述 2 实验环境配置 3 实验过程 3.1 Poetic Walks 3.1.1 Get the code and prepare Git repository 3.1.2 Proble ...
- HIT 软件构造 lab3实验报告
2020年春季学期 计算机学院<软件构造>课程 Lab 3实验报告 姓名 赵旭东 学号 1180300223 班号 1803002 电子邮件 1264887178@qq.com 手机号码 ...
- 哈工大软件构造Lab1实验报告
2020年春季学期 计算机学院<软件构造>课程 Lab 1实验报告 目录 1 实验目标概述... 1 2 实验环境配置... 1 3 实验过程... 1 3.1 Magic Squares ...
- [HITSC]哈工大2020春软件构造Lab3实验报告
Github地址 1 实验目标概述 本次实验覆盖课程第 3.4.5 章的内容,目标是编写具有可复用性和可维护 性的软件,主要使用以下软件构造技术: 子类型.泛型.多态.重写.重载 继承.代理.组合 常 ...
- 软件构造Lab2实验总结
目录 一.关于split 1.split方法简介 2.带数的split方法 3.避免划分后数组开头出现空串的一个思路 二.正则表达式判断字词 1.unicode类别 2.构造正则表达式 三.关于复用 ...
- 关于hit软件构造lab2实验后回顾
1 实验目标概述 本次实验训练抽象数据类型(ADT)的设计.规约.测试,并使用面向对象编程(OOP)技术实现ADT.具体来说: 针对给定的应用问题,从问题描述中识别所需的ADT: 设计ADT规 ...
最新文章
- RedHat Linux网络配置过程笔记
- Windows 技术篇 - win10复制文件或文件夹时出错,提示“文件或目录损坏且无法读取“问题解决。windows驱动器、磁盘修复方法
- HDU-2476 String painter 区间DP
- tomcat开启SSL8443端口的方法
- shell 命令详解
- 苹果发布iOS 13.2测试版 iPhone 11拍照更牛了
- Python 和 奥数 — 同余法求数值
- fdisk 分区/格式化/挂载
- 20210218CTF伪协议绕过file_get_contents(bugkuctf的web21御结冰城感想)
- C语言 与 或 非
- python网络编程【二】(使用TCP)
- 计算机网络技术—知识点汇总
- 电脑狂、理论家、情报员……你是哪种类型的软件工程师?
- 第一次考CSP(第27次CCF计算机软件能力认证)
- 莫比乌斯(Mobius)反演知识整合
- 2021年线上线下融合会议需求增长,Hilton EventReady Hybrid Solutions将启动
- Between Worlds 3 太阳与地球
- 苹果网速4g测试软件,App Store 上的“测网速 - SpeedTest 网速测试大师”
- 虚拟服务器连接不了,虚拟主机不能访问怎么办
- DAS SAN NAS 存储区别及应用场景
热门文章
- 京东商城 最具争议的B2C
- VS无法打开 NuGet包
- Minio 图片永久访问的问题处理
- idea Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Progra
- 链路追踪jaeger
- 一步一步学ROP之linux_x86篇
- Moodle平台使用问题集锦
- 陈皓:谈谈数据安全和云存储
- Win10一更新,我的GT730显卡驱动就异常
- Json系列之二 json to bean(JSONObject类详解)