软件构造lab2 - 实验报告

  • 1.实验目标概述
  • 2.环境配置
  • 3.实验过程
    • 3.1Poetic Walks
      • 3.1.1Get the code and prepare Git repository
      • 3.1.2Problem 1: Test Graph
      • 3.1.3Problem 2: Implement Graph
        • 3.1.3.1Implement ConcreteEdgesGraph
        • 3.1.3.2Implement ConcreteVerticesGraph
      • 3.1.4Problem 3: Implement generic Graph
        • 3.1.4.1Make the implementations generic
        • 3.1.4.2Implement Graph.empty()
      • 3.1.5Problem 4: Poetic walks
        • 3.1.5.1Test GraphPoet
        • 3.1.5.2Implement GraphPoet
        • 3.1.5.3Graph poetry slam
      • 3.1.6Before you’re done
    • 3.2Re-implement the Social Network in Lab1
      • 3.2.1FriendshipGraph类
      • 3.2.2 Person类
      • 3.2.3客户端main()
      • 3.2.4测试用例
      • 3.2.5提交至Git仓库
    • 3.3Playing Chess
      • 3.3.1ADT设计/实现方案
      • 3.3.2主程序MyChessAndGoGame设计/实现方案
      • 3.3.3 ADT和主程序的测试方案
  • 4.实验进度记录
  • 5.实验过程中遇到的困难与解决途径
  • 6.实验过程中收获的经验、教训、感想
    • 6.1实验过程中收获的经验和教训
    • 6.2针对以下方面的感受

1.实验目标概述

本次实验训练抽象数据类型(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)
https://github.com/ComputerScienceHIT/Lab2-1180300119

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/Spring2020_HITCS_SC_Lab2/tree/master/P1
⚫ 在作业描述中若遇到“commit and push”的要求,请将你的代码 push 到你的 GitHub Lab2 仓库中。
⚫ MIT 作业页面提及的文件路径,请按照下表的目录结构进行调整。例如
“test/poet”应为“test/P1/poet”,
“src/poet”应为“src/P1/poet”。
⚫ 其他步骤请遵循 MIT 作业页面的要求。
https://github.com/rainywang/Spring2020_HITCS_SC_Lab2/tree/master/P1获得实验代码。

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默认的实例类。

3.1.3Problem 2: Implement Graph

以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
GraphInstanceTest
// Testing strategy
// 此test与具体实现无关
//
// 测试Graph.add()方法:
// 如果未存在该顶点,修改graph,加入顶点集,返回True
// 否则图不变,返回False
//
// 测试Graph.remove()方法:
// 如果不存在该点,图不变,返回False
// 否则删除该点,并删除相邻的边,返回True
//
// 测试Graph.sources()方法
// graph为空
// set点和边,创建一个map进行同步操作
// 与map比较 , 对应关系是否正确
//
// 测试Graph.targets()方法
// graph为空
// set点和边,创建一个map进行同步操作
// 与map比较 , 对应关系是否正确
//
// 测试Graph.set()方法:
// 分开测试weight的情况 : 0 , > 0
// weight为0检查边是否被删除
// weight > 0 , 检查边是否被添加或更新
// 比较输出tostring
//
// 测试Graph.vertices()方法:
// graph为空
// graph添加顶点后,删除定点
// 观察顶点的数量
//
//测试All:
//graph为空
//graph进行所有操作
//与期望比较

3.1.3.1Implement ConcreteEdgesGraph

一.补全ConcreteEdgesGraph部分:
// Abstraction function:
// 通过对graph中边和点的抽象
// 边的类中包含了source,target,weight
// 带方向的边
// 构成有向图
// Representation invariant:
// 不变量在于隐含的数学关系
// n个点,最多构成n*(n-1)/2条有向边
// Safety from rep exposure:
// 在可操作的情况下,所有的变量都定义为private , final
// 防御式拷贝
// 使用Collections.unmodifiableSet等方法
构造函数:
public ConcreteEdgesGraph() {
}

private void checkRep():每个function返回前检查rep不变
@Override public boolean add(L vertex)
如果在vertices的Set集合中成功添加了顶点string,则返回true,如果重复,返回false。
private int FindEdge(L source, L target)
为了找到edges里是否存在一条指定的边,该边的source和target必须与传入的参数相等,返回该边的index
@Override public int set(L source, L target, int weight)
根据source和target调用findEdge()返回指定边的index。当weight>0,如果index<0,没找到指定边,则添加顶点和边;如果index>0并且找到了指定边,则update这条边。当weight>0且index<0时,找到了指定边,将其删除/最后checkRep()。
@Override public boolean remove(L vertex)
如果不含删除点,返回false。否则遍历edges,如果某个edge的source或是target与vertex相等,则删除该边。最后删除vertex点。并checkRep。

@Override public Set vertices()
{
return Collections.unmodifiableSet(vertices);
}
返回Set vertices,使用Collections.unmodifiableSet()方法保证safety from rep exposure。

@Override public Map<L, Integer> sources(L target)
建立一个map,遍历edges,如果某个edge的edge.getTarget()和传入参数target相等,则将该边的source和weight存入map中。

@Override public Map<L, Integer> targets(L source)
建立一个map,遍历edges,如果某个edge的edge.getSource()和传入参数source相等,则将该边的target和weight存入map中。

public String toString()
graph空,则返回“This is An Empty Graph”.graph不为空,则将每个边的toString连接起来

二.实现class Edge部分:
private final Set vertices = new HashSet();
private final List<Edge> edges = new ArrayList<Edge>();
// Abstraction function:
// 通过对graph中边和点的抽象
// 边的类中包含了source,target,weight
// 带方向的边
// 构成有向图
// Representation invariant:
// 不变量在于隐含的数学关系
// n个点,最多构成n*(n-1)/2条有向边
// Safety from rep exposure:
// 在可操作的情况下,所有的变量都定义为private , final
// 防御式拷贝
// 使用Collections.unmodifiableSet等方法
构造函数:
public Edge(L source,L target,int w){
this.weight=w;
this.sourceVertex=source;
this.targetVertex=target;
checkRep();
}
public void checkRep() {
assert sourceVertex != null;
assert targetVertex != null;
assert weight>=0;
}
各个field的getter
public L getSource() {
return sourceVertex;
}

public L getTarget() {return targetVertex;
}public int getWeight() {return weight;
}

@Override public String toString()
{
String s = this.getSource() + “->” + this.getTarget() + “:” +this.getWeight();
return s;
}
输出形式:V1->V2:weight
ConcreteEdgesGraphTest
// Testing strategy
//
// 测试ConcreteEdgesGraph().tostring()方法:
// 随意创建一个图
// 用tostring与预期输出比较
//
// 测试Edges:
// 随意创建一条边
// 用tostring与预期输出比较

3.1.3.2Implement ConcreteVerticesGraph

一.ConcreteVerticesGraph部分:
// Abstraction function:
// 将有向加权图描述为多个顶点
// 点之间的映射关系为边
// 边有权值weight
//
// Representation invariant:
// 每个顶点只能存在一个实例
//
// Safety from rep exposure:
// 变量尽可能定义为private和final
// 防御式编程构造函数:
public ConcreteVerticesGraph() {
}

private void checkRep():每个function返回前检查rep不变
@Override public boolean add(L vertex)
如果在vertices的Set集合中成功添加了顶点vertex,则返回true,如果重复,返回false。
@Override public int set(L source, L target, int weight)
判断source和target是否已加入vertices当中,如果没有,添加。用source的setTarget(target, weight)向source的Target里添加,用target的setSource(source, weight)向target的Source里添加,chechRep()。返回原权值。

@Override public boolean remove(L vertex)
如果vertices()不包含vertex,返回false。否则遍历所有点,如果某点的Target或Source与vertex存在映射关系,则将这种删除vertex。最后将vertex对应的点从vertices中删除即可。

@Override public Set vertices()
{
Set Vertices = new HashSet<>();
for(Vertex s:vertices)
Vertices.add(s.getName());
return Vertices;
}
新建一个Set Vertices,将Set vertices里的每个元素加入Vertices,返回Vertices。

@Override public Map<L, Integer> sources(L target)
如果找不到target对应的点,返回Collections.emptyMap()。否则调用Collections.unmodifiableMap()返回target对应的源点图。

@Override public Map<L, Integer> targets(L source)
如果找不到source对应的点,返回Collections.emptyMap()。否则调用Collections.unmodifiableMap()返回source对应的目标点图。

public String toString()
graph空,则返回“This is An Empty Graph”.graph不为空,则将每个点的toString连接起来

三.实现class Vertex部分:
private L name;
private Map<L, Integer>Source = new HashMap<L, Integer>();
private Map<L, Integer>Target = new HashMap<L, Integer>();

// Abstraction function:
// 顶点类刻画图的关键要素
// 使用HashMap存取映射关系
// key为该点的source或target , value为该点的weight
//
// Representation invariant:
// 每个顶点的source或target不能是自身
// HashMap中的values必须不小于0
//
// Safety from rep exposure:
// 所有fields是private final
// 为每个field设置getter和setter
// String是imutable类型
// 防御性编程

构造函数:
public Vertex(L name){
this.name=name;
}
private void checkRep() {
assert Source!=null;
assert Target!=null;
assert name!=null;
}
各个field的getter
public Map<L, Integer> getSource() {
return Source;
}

public Map<L, Integer> getTarget() {return Target;
}public L getName() {return name;
}

各个Source和Target的setter
public int setSource(L target2, int weight)
{
if(weight >= 0)
{
int Weight = 0;
for (Map.Entry<L, Integer> entry : Source.entrySet())
{
if(entry.getKey() == target2)
{
if(weight == 0)
{
Weight = entry.getValue();
Source.remove(target2);
}
else
{
Weight = entry.getValue();
Source.put(target2, weight);
}
return Weight;
}
}
Source.put(target2, weight);
return 0;
}
return -1;
}
设置到该点的source,处理同ConcreteVerticesGraph中set方法,weight = 0 , source存在 :移除,weight > 0 , source存在 :更新,weight > 0 , source不存在 :添加
public int setTarget(L source2, int weight)
{
if(weight >= 0)
{
int Weight = 0;
for (Map.Entry<L, Integer> entry : Target.entrySet())
{
if(entry.getKey() == source2)
{
if(weight == 0)
{
Weight = entry.getValue();
Target.remove(source2);
}
else
{
Weight = entry.getValue();
Target.put(source2, weight);
}
return Weight;
}
}
Target.put(source2, weight);
return 0;
}
return -1;
}
设置到该点的target,处理同ConcreteVerticesGraph中set方法,weight = 0 , source存在 :移除,weight > 0 , source存在 :更新,weight > 0 , source不存在 :添加
@Override public String toString()
{
String s = this.getSource() + “->” + this.getTarget() + “:” +this.getWeight();
return s;
}
输出形式:
source->getname:weight
getname->target:weight

ConcreteVerticesGraphTest
// Testing strategy
//
// 测试Graph.add()方法:
// 如果未存在该顶点,修改graph,加入顶点集,返回True
// 否则图不变,返回False
//
// 测试Graph.remove()方法:
// 如果不存在该点,图不变,返回False
// 否则删除该点,并删除相邻的边,返回True
//
// 测试Graph.sources()方法
// graph为空
// set点和边,创建一个map进行同步操作
// 与map比较 , 对应关系是否正确
//
// 测试Graph.targets()方法
// graph为空
// set点和边,创建一个map进行同步操作
// 与map比较 , 对应关系是否正确
//
// 测试Graph.set()方法:
// 分开测试weight的情况 : 0 , > 0
// weight为0检查边是否被删除
// weight > 0 , 检查边是否被添加或更新
// 比较输出tostring
//
// 测试Graph.vertices()方法:
// graph为空
// graph添加顶点后,删除定点
// 观察顶点的数量
//
//测试All:
//graph为空
//graph进行所有操作
//与期望比较
//
// 测试ConcreteEdgesGraph().tostring()方法:
// 随意创建一个图
// 用tostring与预期输出比较
//
// 测试Edges:
// 随意创建一条边
// 用tostring与预期输出比较

3.1.4Problem 3: Implement generic Graph

3.1.4.1Make the implementations generic

public class ConcreteEdgesGraph implements Graph
class Edge
public class ConcreteVerticesGraph implements Graph
class Vertex
public interface Graph
更新两个实现以支持任何类型的顶点标签,使用占位符L代替String。
已声明类型为Edge或的变量List。那些将需要成为Edge和List<Edge>。

3.1.4.2Implement Graph.empty()

public static <L> Graph<L> empty() {Graph<L> graph = new ConcreteEdgesGraph<L>();return graph;
}

3.1.5Problem 4: Poetic walks

3.1.5.1Test GraphPoet

// Testing strategy
// 给定一个input。从文件中读取poet,调用Graph.poem()后
// 观察输出与预期是否相等

使用文件输入

3.1.5.2Implement GraphPoet

private final Graph<String> graph = Graph.empty();
private final List<String> words = new ArrayList<String>();// Abstraction function:
// 将输入的文本单词提取作为顶点
// 构建有向图
// 转化为poem
//
// Representation invariant:
// 输入的文本words不为空
// 有向图不为null
//
// Safety from rep exposure:
// 所有fields都是private final

// 防御式编程
public GraphPoet(File corpus) throws IOException
先检测文件是否为空。如果不为空,从语料库的图形中创建一个新的poet。读文件,并把文件中的单词存在words中。使用BufferedReader读取文本文件中的数据,类Scanner用于将输入的文本分解成多个部分。
然后调用Graph类中的方法,将单词转化为图,添加顶点,set边,其中权值全部设置为1,如果重复权值加1.
public void checkRep() {
assert !graph.vertices().isEmpty();
}
检查graph的vertices()是否为空
public String poem(String input)
对input进行扩展,规则是:如果相邻两个单词a和b,在语料库中存在一个单词c使a->c和c->b都存在,则将c插入a和b之间;如果存在多个满足条件的c,取边权a->c较大的。
public String poem(String input) {
String[] inputWords = input.split("\s");
StringBuilder poem = new StringBuilder(input);
int fromIndex = 0;
for (int i = 0; i < inputWords.length - 1; i++)
{
int weight = 0;
String Bridge = “”;
Set wordTargets = graph.targets(inputWords[i].toLowerCase()).keySet();
Set wordSources = graph.sources(inputWords[i + 1].toLowerCase()).keySet();
Set probableBridges = new HashSet();
probableBridges.addAll(wordSources);
probableBridges.retainAll(wordTargets);
for(String key :probableBridges)
{
if(graph.targets(inputWords[i].toLowerCase()).get(key) + graph.sources(inputWords[i + 1].toLowerCase()).get(key) > weight)
{
weight = graph.targets(inputWords[i].toLowerCase()).get(key) + graph.sources(inputWords[i + 1].toLowerCase()).get(key);
Bridge = key;
}
}
if(weight > 0)
{
fromIndex = poem.indexOf(inputWords[i+1], fromIndex);
poem.insert(fromIndex, Bridge + " ");
}
}
checkRep();
return poem.toString();
}
首先将input按空白字符划分为各个单词,不区分大小写,线性扫描,调用graph.target()和source()求交集找出所有可能的“桥”(上面说到的c),然后找到边权最大的一个,插入。最后返回扩展好的字符串。注意在插入bridge时使用poem.indexOf(inputWords[i+1], fromIndex);和poem.insert(fromIndex, Bridge + " ");
public String toString()
{
return graph.toString();
}

3.1.5.3Graph poetry slam

更新语料库文件中的字符串和输入的字符串input

3.1.6Before you’re done

请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。
在这里给出你的项目的目录结构树状示意图。

3.2Re-implement the Social Network in Lab1

该实验基于在p1定义的Graph及其两种实现,重新实现Lab1中的 FriendshipGraph类。我们需要尽可能复用ConcreteEdgesGraph或 ConcreteVerticesGraph中已经实现的add()和set()方法,而不是从零开始。另外基于所选定的 ConcreteEdgesGraph 或 ConcreteVerticesGraph的rep来实现,而不能修改父类的rep。

3.2.1FriendshipGraph类

private final ConcreteEdgesGraph<Person> person = new ConcreteEdgesGraph<Person>();public void addVertex(Person newperson) throws Exception
{for(Person s:person.vertices()){if(s.getName().equals(newperson.getName()))throw new Exception("输入了重复的名字");}

}
person.add(newperson);
使用graph的add()方法进行操作

public void addEdge(Person person1, Person person2) throws Exception
{if(!person.targets(person1).containsKey(person2))person.set(person1, person2, 1);elsethrow new Exception("输入了重复的关系");
}

使用graph的set()方法进行操作

 public int getDistance(Person person1, Person person2)

使用广度优先遍历,具体实现同lab1,注意使用person.targets(top).keySet()进行简化

3.2.2 Person类

public class Person {
String name;
boolean visit = false;
public Person(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}
}
没什么可说的,非常简单

3.2.3客户端main()

Lab1实验手册给出。

3.2.4测试用例

// Testing strategy
// 构成一张人际关系网

// 观察输出与预期是否相等
分别测试重点,重边,完整图以及各种距离的测试

3.2.5提交至Git仓库

如何通过Git提交当前版本到GitHub上你的Lab3仓库。
在这里给出你的项目的目录结构树状示意图。

3.3Playing Chess

3.3.1ADT设计/实现方案

Position
private int x;
private int y;
// Abstraction function:
// Position代表棋子在棋盘上的位置,x代表位置的横坐标,y代表位置的纵坐标
//
// Representation invariant:
// position不能为空
//
// Safety from rep exposure:
// 所有fields都是 private and final
// 使用immutable数据类型
构造函数:
public Position(int x,int y)
{
this.x=x;
this.y=y;
}
getter方法以及判重方法equal
Piece
final private String owner;
final private String type;
private int state = 0;
private Position position = new Position(-1, -1);
// Abstraction function:
// Piece代表棋盘上的棋子 ,type代表棋子的类型,pieceState代表棋子的放置状态,
// position代表棋子坐标, owner代表棋子所有者
//
// Representation invariant:
// piece不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private
// 使用immutable数据类型
构造函数:
public Piece(String belong,String type)
{
this.owner = belong;
this.type = type;
}
各field的getter,以及state和position的setter
Board
final private int type;
final private int size;
private Piece[][] pieces ;
private boolean[][] placed;
// Abstraction function:
// Board 代表棋子所处的棋盘,size代表了棋盘的边长,type代表了棋盘类型
// pieces代表棋盘每个位置上的棋子,placed代表棋盘每个位置上有无棋子的状态
//
// Representation invariant:
// Board不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private and final
// 使用immutable数据类型
构造函数:
public Board(int type, int size)
{
this.type = type;
this.size = size;
size += type;
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;
}
}
各个field的getter,check()检查是否超出棋盘范围,pieces[][],placed[][]的setter
Player
final private String name;
private Set pieces = new HashSet();
private List history = new LinkedList();
// Abstraction function:
// Player映射操作pieces的玩家,name为玩家的名称,
// pieces代表玩家所拥有的在棋盘上的棋子,history的字符串为玩家历史
//
// Representation invariant:
// Player不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private and final
// 使用immutable数据类型
构造函数:
public Player(String name) {
this.name=name;
}
各个field的getter
public boolean addPiece(Piece newPiece)
给玩家所属棋子集合pieces里添加一枚棋子,重复返回false
public boolean removePiece(Piece removePiece)
从玩家所属棋子集合pieces里删除一枚棋子,集合中没有返回false
public int getNumberOfPieces()
{
int num =0;
for(Piece p:pieces)
num++;
return num;
}
获得玩家所属棋子集合pieces里棋子个数
public void addStep(String step)
{
if(!step.isEmpty())
history.add(step);
}
向玩家的历史记录集合history添加输入step
Action
有两个实例类ChessAction和GoAction
ChessAction
private final int boardsize = 8;
private Player player1;
private Player player2;
private Board chessBoard = new Board(0, boardsize);
// Abstraction function:
// ChessAction映射为棋手的动作,chessBoard是一个Board的对象
// player1,player2分别为玩家1,2,boardsize为棋盘大小
//
// Representation invariant:
// ChessActionAction和Board不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private and final
// 使用immutable数据类型
// placePiece和removePiece象棋不能使用默认为false
构造函数:
public ChessAction(String p1, String p2)
{
this.player1 = new Player(p1);
this.player2 = new Player(p2);
checkRep();
}
各个field的getter
public void checkRep()
检查是否超出棋盘范围
public void boardIni()
初始化国际象棋棋盘,设置32个棋子,向两个玩家棋子集合中添加对应的16个棋子,并且在chessboard对应坐标上进行设置。
public void PrintBoard()
打印棋盘
private Player Other(String p)
根据输入名字返回另一个玩家名字
public boolean check1(Player player,Piece piece)
检查该棋子是否为该玩家所有
public boolean check2(Position position)
检查该位置是否有棋子
public boolean check3(Piece piece)
检查该棋子状态
public boolean check4(Position position1, Position position2)
检查两个位置是否相同
public boolean placePiece(Player player, Piece piece, Position position)
象棋没有该功能,默认返回false
public boolean removePiece(Player player, Position position)
象棋没有该功能,默认返回false
public boolean movePiece(Player player, Position oldPosition, Position newPosition)
给定“棋手、一颗棋子、指定位置的横坐标、指定位置的纵坐标”作为输入参数,将该棋手的该颗棋子放置在棋盘上
异常情况,例如:该棋子并非属于该棋手、指定的位置超出棋盘的范围、指定位置已有棋子、所指定的棋子已经在棋盘上等,返回false
否则,将棋子落在指定位置,返回true
public boolean eatPiece(Player player, Position position1, Position position2) throws Exception
给定“棋手、两个位置横纵坐标”,将第一个位置上的棋子移动至第二个位置,第二个位置上原有的对手棋子从棋盘上移除,然后从对手棋子集合中移除该棋子。
异常情况,例如:指定的位置超出棋盘的范围、第一个位置上无棋子、第二个位置上无棋子、两个位置相同、第一个位置上的棋子不是自己的棋子、第二个位置上的棋子不是对方棋子、等.返回false。
否则,返回true。
GoAction
private final int boardsize = 18;
private Player player1;
private Player player2;
private Board goBoard = new Board(1, boardsize);
// Abstraction function:
// GoAction映射为棋手的动作,goBoard是一个Board的对象
// player1,player2分别为玩家1,2,boardsize为棋盘大小
//
// Representation invariant:
// GoActionAction和Board不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private and final
// 使用immutable数据类型
// movePiece和eatPiece围棋不能使用默认为false
构造函数:
public GoAction(String p1, String p2)
{
this.player1 = new Player(p1);
this.player2 = new Player(p2);
checkRep();
}
各个field的getter
public void checkRep()
检查是否超出棋盘范围
public void boardIni()
初始化围棋棋盘,棋盘上没有任何棋子
public void PrintBoard()
打印棋盘
private Player Other(String p)
根据输入名字返回另一个玩家名字
public boolean check1(Player player,Piece piece)
检查该棋子是否为该玩家所有
public boolean check2(Position position)
检查该位置是否有棋子
public boolean check3(Piece piece)
检查该棋子状态
public boolean check4(Position position1, Position position2)
检查两个位置是否相同
public boolean placePiece(Player player, Piece piece, Position position)
给定“棋手、一颗棋子、指定位置的横坐标、指定位置的纵坐标”作为输入参数,将该棋手的该颗棋子放置在棋盘上,然后将该棋子加入棋手所拥有的棋子集合。
异常情况,例如:该棋子并非属于该棋手、指定的位置超出棋盘的范围、指定位置已有棋子、所指定的棋子已经在棋盘上等,返回false
否则,将棋子落在指定位置,返回true
public boolean removePiece(Player player, Position position)
给定“棋手、一个位置的横纵坐标”,将该位置上的对手棋子移除,然后从对手棋子集合中移除该棋子。
异常情况,例如:该位置超出棋盘的范围、该位置无棋子可提、所提棋子不是对方棋子、等,返回false。
否则,移除棋子并返回true。
public boolean movePiece(Player player, Position oldPosition, Position newPosition)
围棋没有该功能,默认返回false
public boolean eatPiece(Player player, Position position1, Position position2) throws Exception
围棋没有该功能,默认返回false
Game
private String gameName;
private Board gameBoard;
private Action gameAction;
private Player player1, player2;
// Abstraction function:
// Game代表了一局游戏,gameBoard映射为game中的棋盘,gameName是游戏名称
// gameAction为棋手的操作,游戏玩家是player1和player2.
//
// Representation invariant:
// Game不能映射为空,Board不能映射为空,Action不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private and final
// 使用immutable数据类型
构造函数:
public Game(String gamename, String playerName1, String playerName2)
{
this.gameName = gamename;
if(gamename.equals(“chess”))
gameAction = new ChessAction(playerName1, playerName2);
else
gameAction = new GoAction(playerName1, playerName2);
gameAction.boardIni();
gameBoard = gameAction.getBoard();
this.player1 = new Player(playerName1);
this.player2 = new Player(playerName2);
}
各个field的getter
public boolean check(int x,int y)
{
return gameBoard.check(x, y);
}
检查是否超出棋盘范围
public boolean placePiece(Player player, Piece piece, Position position)
{
return gameAction.placePiece(player, piece, position);
}
调用Action的placePiece
public boolean movePiece(Player player, Position oldPosition, Position newPosition) throws Exception
{
return gameAction.movePiece(player, oldPosition, newPosition);
}
调用Action的movePiece
public boolean removePiece(Player player, Position position) throws Exception
{
return gameAction.removePiece(player, position);
}
调用Action的removePiece
public boolean eatPiece(Player player, Position position1 , Position position2) throws Exception
{
return gameAction.eatPiece(player, position1, position2);
}
调用Action的eatPiece
public Piece getOccupationOfPosition(Position position) throws Exception
查看输入位置上的棋子类型
public void printBoard()
打印棋盘
public int getNuberOfPiece1()
获得玩家一棋子集合中的棋子数量
public int getNuberOfPiece2()
获得玩家二棋子集合中的棋子数量
public String getHistorySteps(String playerName)
获得输入玩家的历史记录

3.3.2主程序MyChessAndGoGame设计/实现方案

public static void main(String[] args) {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("chess(国际象棋)"+"\t"+"go(围棋):");gameType = in.nextLine();if(gameType.equals("chess") || gameType.equals("go"))break;else continue;}System.out.println("请输入玩家1的名字:");String playerName1;playerName1 = in.nextLine();System.out.println("请输入玩家2的名字:");String playerName2;playerName2 = in.nextLine();Game game = new Game(gameType, playerName1, playerName2);if (gameType.contentEquals("chess")) startChessGame(game);else startGoGame(game);in.close();

}
Main函数进行游戏初始化,
围棋进行函数
private static void startGoGame(Game game) {
while(true)
{
printGoMenu();
System.out.print(“玩家 " + game.getPlayer1().getName() + " 请输入选项:”);
Scanner in = new Scanner(System.in);
String choice = in.nextLine();
chooseOfGo(game, choice, game.getPlayer1(), in, 0);
System.out.println();
if (choice.equals(“end”)) in.close();

     printGoMenu();System.out.print("玩家 " + game.getPlayer2().getName() + " 请输入选项:");String choice2=in.nextLine();chooseOfGo(game, choice2, game.getPlayer2(), in, 1);System.out.println();if (choice.equals("end")) in.close();}
}

围棋菜单
private static void printGoMenu() {
System.out.println(“1.落子”);
System.out.println(“2.提子”);
System.out.println(“3.查询某个坐标点的占用情况”);
System.out.println(“4.查看玩家棋子总数”);
System.out.println(“5.查看棋盘”);
System.out.println(“6.跳过”);
System.out.println(“7.end”);
}
象棋进行函数
private static void startChessGame(Game game) {
while(true)
{
printChessMenu();
System.out.print(“玩家 " + game.getPlayer1().getName() + " 请输入选项:”);
Scanner in = new Scanner(System.in);
String choice = in.nextLine();
chooseOfChess(game, choice, game.getPlayer1(), in);
System.out.println();
if (choice.equals(“end”)) in.close();

     printChessMenu();System.out.print("玩家 " + game.getPlayer2().getName() + " 请输入选项:");String choice2=in.nextLine();chooseOfChess(game, choice2, game.getPlayer2(), in);System.out.println();if (choice.equals("end")) in.close();}
}

象棋菜单
private static void printChessMenu() {
System.out.println(“1.移子”);
System.out.println(“2.吃子”);
System.out.println(“3.查询某个坐标点的占用情况”);
System.out.println(“4.查看玩家棋子总数”);
System.out.println(“5.查看棋盘”);
System.out.println(“6.跳过”);
System.out.println(“7.end”);
}
围棋运行实况

象棋运行实况

3.3.3 ADT和主程序的测试方案

介绍针对各ADT的各方法的测试方案和testing strategy。
介绍你如何对该应用进行测试用例的设计,以及具体的测试过程。
BoardTest PieceTest PlayerTest PositionTest
// Testing strategy
// 测试getter与setter方法
// 观察输出与预期是否相等
简单测试基础方法的实现是否正确

GameTest
// Testing strategy
// 分别测试围棋与象棋两种棋类游戏的实现
// 观察输出与预期是否相等
//
// 象棋
// 测试Game ChessAction的getter
// 测试Game ChessAction的movePiece,观察输出与预期是否相等
// 测试Game ChessAction的eatPiece, 观察输出与预期是否相等
// 测试Game ChessAction的placePiece, 输出为false
// 测试Game ChessAction的removePiece, 输出为false
//
// 围棋
// 测试Game GoAction的getter
// 测试Game GoAction的removePiece,观察输出与预期是否相等
// 测试Game GoAction的placePiece, 观察输出与预期是否相等
// 测试Game GoAction的movePiece, 输出为false
// 测试Game GiAction的eatPiece, 输出为false
//
// 测试Game的gethistory
// 分别建立围棋象棋两种游戏
// 让玩家进行若干操作
// 观察history与先前操作是否相等
简单测试基础方法的实现是否正确
分别对围棋,象棋两种棋类运动进行测试,并且对要求的非法输入进行测试,看是否能全部排除

4.实验进度记录

请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。

5.实验过程中遇到的困难与解决途径

6.实验过程中收获的经验、教训、感想

6.1实验过程中收获的经验和教训

对于新的编程思路以及编程习惯有了新的理解,对于adt的适用以及好处和自我思考建立有了新的看法

6.2针对以下方面的感受

(1)面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
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。
工程量适中,难度较大,deadline合理
(8)《软件构造》课程进展到目前,你对该课程有何体会和建议?
建议多使用中文,英文理解起来有困难

软件构造lab2 - 实验报告相关推荐

  1. 哈工大2020软件构造Lab2实验报告

    本项目于3.17日实验课验收,请放心参考 参考时文中有给出一些建议,请查看 基本更新完成 2020春计算机学院<软件构造>课程Lab2实验报告 Software Construction ...

  2. HIT 软件构造 lab2实验报告

    2020年春季学期 计算机学院<软件构造>课程 Lab 2实验报告 学号 1180300223 班号 1803002 目录 1 实验目标概述 1 2 实验环境配置 1 3 实验过程 1 3 ...

  3. 软件构造 Lab2 实验报告

    2021年春季学期 计算学部<软件构造>课程 Lab 2实验报告 姓名 王雨宁 学号 1190201118 班号 1903006 电子邮件 1756840811@qq.com 手机号码 1 ...

  4. 哈工大软件构造lab2实验报告

    1 实验目标概述 2 实验环境配置 3 实验过程 3.1 Poetic Walks 3.1.1 Get the code and prepare Git repository 3.1.2 Proble ...

  5. HIT 软件构造 lab3实验报告

    2020年春季学期 计算机学院<软件构造>课程 Lab 3实验报告 姓名 赵旭东 学号 1180300223 班号 1803002 电子邮件 1264887178@qq.com 手机号码 ...

  6. 哈工大软件构造Lab1实验报告

    2020年春季学期 计算机学院<软件构造>课程 Lab 1实验报告 目录 1 实验目标概述... 1 2 实验环境配置... 1 3 实验过程... 1 3.1 Magic Squares ...

  7. [HITSC]哈工大2020春软件构造Lab3实验报告

    Github地址 1 实验目标概述 本次实验覆盖课程第 3.4.5 章的内容,目标是编写具有可复用性和可维护 性的软件,主要使用以下软件构造技术: 子类型.泛型.多态.重写.重载 继承.代理.组合 常 ...

  8. 软件构造Lab2实验总结

    目录 一.关于split 1.split方法简介 2.带数的split方法 3.避免划分后数组开头出现空串的一个思路 二.正则表达式判断字词 1.unicode类别 2.构造正则表达式 三.关于复用 ...

  9. 关于hit软件构造lab2实验后回顾

    1 实验目标概述 本次实验训练抽象数据类型(ADT)的设计.规约.测试,并使用面向对象编程(OOP)技术实现ADT.具体来说:  针对给定的应用问题,从问题描述中识别所需的ADT:  设计ADT规 ...

最新文章

  1. 项目: 用C语言写一个精美图形化的音乐播放器【C++ / C】
  2. 如何选择MBA教育 读MBA前必答10个问题
  3. UVALive 8518 - Sum of xor sum
  4. jar打包 jar line too long 异常处理方法
  5. inline-block是html5,详解CSS display:inline-block的应用
  6. IntelliJ IDEA中怎么创建xml文件?
  7. SAP License:关于客户寄售的问题
  8. 生成模型VS判别模型
  9. Turbo码 - 接近完美的编码
  10. arcmap拓扑错误检查器不亮_ARCGIS拓扑检查步骤与修正拓扑错误技巧
  11. 用户根据短信验证码注册
  12. Windows 10, version 21H2 (released Nov 2021) 简体中文版、英文版(企业版)下载
  13. vivo手机便签扫描功能怎么识别图片文字转为电子文字
  14. 金蝶一直显示服务器未启动怎么办,金蝶服务器数据库未启动怎么办
  15. Deepgreen/Greenplum 删除节点步骤
  16. SIFT四部曲之——极值检测和定位
  17. Solr Tokenizers分词器介绍
  18. 视频教程-网络安全与渗透测试工程师-渗透测试
  19. 小白如何选择采集器-爬虫
  20. Java基础-程序基础

热门文章

  1. 交换机端口假死(err-disable)解决方法
  2. JSON long 型 数字过长精度丢失解决
  3. python画中秋月饼,用turtle海龟库画中秋月饼
  4. 天眼和计算机科学相关吗,中国天眼重要成果发布,两篇Nature都与它有关
  5. 如何用小程序快速创业?
  6. 提交测试流程和【开发提测申请模板】
  7. [渝粤教育] 南京工业大学 有机化学实验 参考 资料
  8. 2021年中式烹调师(初级)最新解析及中式烹调师(初级)免费试题
  9. Java实现 LeetCode 292 Nim游戏
  10. Day600601.马踏棋盘算法 -数据结构和算法Java