软件构造Lab2-实验报告

  1. 实验目标概述

本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现ADT。具体来说:

⚫ 针对给定的应用问题,从问题描述中识别所需的ADT

⚫ 设计ADT规约(pre-conditionpost-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

  1. 对给定的接口Graph,完成他的两个实例类ConcretVerticesGraph和ConcreateEgdesGraph:

(1)首先分别构造两个实例类的rep类Vertex和Edge;

(2)用String作为L的特例,分别在两个实例类中实现Graph接口中的各个方法,写对应的test文件以确保其正确性;

(3)将所有String变量替换为泛型L。

2. 对给定的文本文件,按照给定规则建立有向图,调用Graph作为存储语料库的数据结构,利用该结构,对输入的字符串进行扩充。

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 [email protected]:ComputerScienceHIT/Lab2-1173710111.git

Git pull origin master

Git add .

Git commit -m “xxx”

Git push origin master

 

3.1.2Problem 1: Test Graph 

静态 Graph.empty()方法的测试策略和测试都在GraphStaticTest.java,为了运行这个测试,首先要修改Graph empty()为:

 public static String Graph empty() {

        Graph graph = new ConcreteEdgesGraph();

return graph;

 }

这里以ConcreteEdgesGraph作为Graph默认的实例类,也可以用ConcreteVerticesGraph,二者是等价的

3.1.3 Problem 2: Implement Graph 

3.1.3.1Implement ConcreteEdgesGraph

(1)实现 Edge<String>类

  1. rep:

private int weight;

边的权值

private String sourceVertex;

边的起点

private String targetVertex;

边的终点

  1. constructor:

public Edge(L source,L target,int w){

this.weight=w;

this.sourceVertex=source;

this.targetVertex=target;

}

 

  1. function:

getter

三个域的getter,因为规定Edge是Immutable的所以没有setter。

public void checkRep() {

assert sourceVertex!=null;

assert targetVertex!=null;

assert weight>=0;

}

每个getter返回前检查该边的起点、终点、权值是否有空的。

@Override public String toString()

以字符串的形式直观地展示该边的起点、终点和边权,格式为:

source->target [weight]

(2)实现ConcreteEdgesGraph类

  1. rep:

private final Set<Stringvertices

保存图中的所有顶点

private final ListString>> edges

顺序存储图中的所有边

  1. 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 sourceString targetint 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;

  1. 否则,遍历edges,删除起点或终点是该顶点的边,遍历结束后调用list.remove从vertices中删除该顶点。

@Override public Set vertices()

copy一份vertices到新Set中,返回这个Set

@Override public Map sources(L target)

建立一个空Map,遍历edges,找出以target为终点的边edge;

weight=0时表示该边不存在,跳过;

将edge.source->edge.weight加入Map,遍历结束后返回这个Map

@Override public Map 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 emptyInstance();

抽象方法,创建新的空Graph

@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()

  1. 边不存在
  2. 边存在
  3. 边权为0

 

方法调用后,对相关参数(边)调用source方法或target方法的返回值进行检查

@Test public void testVertices()

调用add和remove方法,然后对vertices方法的返回值进行检查

@Test public void testALL()

创建一个较复杂的有向图,调用到每个方法,对rep进行检查

(4)编写测试ConcreteEdgesGraphTest.java

Graph中定义的接口

GraphInstanceTest.java中实现,不必重复

创建一个新的空图 ConcreteEdgesGraph

@Override public Graph emptyInstance() {

        return new ConcreteEdgesGraph();

 }    

    

 public void testToString()

调用Graph中的方法构建一张有向图,测试toString的返回值是否能正确规范地表示这张图。

public void testVertex()

主要测试Edge中的toString方法,测试其返回值能否正确规范地表示这条边的信息。

 

3.1.3.2Implement ConcreteVerticesGraph

(1)实现Vertex

  1. rep:

 private L name;

当前节点的名字(标签)

private Mapsource;

存储所有以当前点为终点的边,key表示边的起点,value表示边权

private Maptarget;

存储所有以当前点为起点的边,key表示边的终点,value表示边权

  1. constructor:

public Vertex(L name){

this.name=name;

}

  1. 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类

  1. rep:

private final Listvertices;

存储图的顶点

  1. 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 s:vertices) {

assert s!=null;

}

}

@Override public boolean add(L vertex)

遍历vertices:

  1. 如果存在标签相同的顶点,返回false;
  2. 否则,创建新顶点(参数vertex),调用list.add加入vertices

@Override public int set(L source, L targetint 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)

  1. 首先遍历vertices,寻找是否存在标签与vertex相等的顶点,如果没有,就返回false;
  2. 如果找到了,判断该顶点的source和target是否为空,不为空的进行遍历,从另一个边的target/source中删除该顶点对应的映射。

 @Override public Set vertices()

copy一份vertices到新Set中,返回这个Set

 @Override public Map sources(L target)

建立一个空Map,遍历vertices,如果某个vertex其vertex.target不为空并且包含了target,就建立vertex->vertex.weight的映射并加入Map;

weight=0时表示该边不存在,跳过;

遍历结束后返回Map

@Override public Map 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 emptyInstance() {

        return new ConcreteVerticesGraph();

    }

 public void testToString()

调用Graph中的方法构建一张有向图,测试toString的返回值是否能正确规范地表示这张图。

public void testVertex()

主要测试Vertex中的toString方法,测试其返回值能否正确规范地表示这个顶点的信息。

 

3.1.4 Problem 3: Implement generic Graph

3.1.4.1 Make the implementations generic

将两个实例类中的所有String类的参数替换为泛型的参数(声明、函数参数、返回值、rep)

3.1.4.2 Implement Graph.empty()

修改Graph.empty为:

 public static  Graph empty() {

        Graph graph = new ConcreteEdgesGraph();

return graph;

}

这里以ConcreteEdgesGraph作为Graph默认的实例类,也可以用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 corpusGraph = Graph.empty();

用于存储文本文件读入后按规则建立的有向图

constructor:

 public GraphPoet(File corpusthrows IOException

根据参数(文件),判断能否正常读入(否则抛出异常),并且调用poemGraph(corpus)方法创建好语料库(有向图)

function:

public void checkRep() {

     assert !corpusGraph.vertices().isEmpty();

    }

各方法返回前检查rep不为空

public Graph 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或者ConcreteVerticesGraph类 实现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 p1Person p2)

使用BFS算法求p1与p2之间的最短距离,BFS需要遍历邻居节点时调用父类接口的targets(p1)就可以获得p1的所有邻居节点。

public Set persons()

copy一份vertices,存储图中所有的顶点,即关系网络中的所有人。

public static void main(String Args[]) 

与Lab1相同

3.2.2Person

由于继承了ConcreteVerticesGraph,所以可以调用父类的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间的关系。

  1. 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 pieces=new HashSet();

存储玩家拥有的所有棋子

private List historyList=new LinkedList();

顺序保存玩家在一局游戏里的走棋历史

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 players 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(gameTypeplayerName1playerName2);

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(gamechoice,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(gamechoice2,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(playerNamexy));

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(playerNamexy));

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(xy);

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 pieces=new ArrayList();

function:

@Before

public void initialize() {

board=new Board(0, 5);

player=new Player("player");

List<Piecepieces=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()

 

 

你可能感兴趣的:(软件构造)