软件构造Lab2

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 实验环境配置

3 实验过程
请仔细对照实验手册,针对三个问题中的每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 Poetic Walks
在这里简要概述你对该任务的理解。
我们需要构造一个有向图Graph,实现spec中的基本功能,并且以边和点两种方式实现接口,并且需要将ADT泛型化。
对给定的接口Graph,完成他的两个实例类ConcretVerticesGraph和ConcreateEgdesGraph:
(1)首先分别构造两个实例类的rep类Vertex和Edge;
(2)用String作为L的特例,分别在两个实例类中实现Graph接口中的各个方法,写对应的test文件以确保其正确性;
(3)将所有String变量替换为泛型L。
对于poet,。对给定的文本文件,按照给定规则建立有向图,调用Graph作为存储语料库的数据结构,利用该结构,对输入的字符串进行扩充
3.1.1 Get the code and prepare Git repository
如何从GitHub获取该任务的代码、在本地创建git仓库、使用git管理本地开发。
创建本地仓库,添加远程仓库:
git init
git add *
git commit -m “提交”
git remote add origin https://github.com/ComputerScienceHIT/Lab2-1183300923.git
git push -u origin master
3.1.2 Problem 1: Test Graph
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
我选用ConcreteEdgesGraph作为Graph的具体实现,如需要测试,将Graph里原empty()方法修改为:
在这里插入图片描述

对于具体方法的测试策略如下:

在这里插入图片描述

测试覆盖率结果如下图所示

在这里插入图片描述

更改未边图后测试结果如下图所示:

在这里插入图片描述

3.1.3 Problem 2: Implement Graph
以下各部分,请按照MIT页面上相应部分的要求,逐项列出你的设计和实现思路/过程/结果。
3.1.3.1 Implement ConcreteEdgesGraph
Edge类的实现
1、Fields:
定义两个private final String类的变量source和target分别存放每条边的弧尾和弧头,一个private final int 类的变量weight表示每条边的权重。

// Abstraction function:
// 以source,target,weight组成的抽象数据型
// Representation invariant:
// weight>0,source和target不为空
// Safety from rep exposure:
// 将信息设置为private final,防御式编程
2、constructor

在这里插入图片描述

3、private void checkRep()
Weight大于零,target和source 不为空
4、三个fields 的get函数;

public L getTarget()
public L getSource()
public int getWeight()
5、重写的toString函数:将有向边输出未如下格式:source->target:1

对于edge类的测试:
/**
* Testing strategy for Edge
* 分别选取两个规约内权重大于零,顶点非空的边看各方法能否返回正确的值;
*/

ConcreteEdgesGraph类的实现
1、Field:
private final Set vertices = new HashSet<>();
private final List<Edge> edges = new ArrayList<>();
// Abstraction function:
//vertices表示图中点,edges表示图中边,共同构成到有向图的映射
// Representation invariant:
//edge 起点,重点不为空且起点终点在集合vertices中并互不相等,weight大于零
//vertices集合中点不为空,且互不相等
//两点间最多只存在一条边
// Safety from rep exposure:
//属性用private final,使用防御式编程
2、private void checkRep()
检查图顶点非空,且边集合中不存在顶点相同的有向边。

在这里插入图片描述

3、具体方法:

public boolean add(L vertex)
思路:如果顶点集合vertices中不包含该顶点则在vertices中加入该顶点返回true,否则返回false。

public int set(L source, L target, int weight)
思路:如果该边的弧尾,弧头相同,则set失败返回零:
	  如果边集合egeds中包含与该有向边两节点相同的有向边则删除该边;
	  Weight=0;返回已删除边的权重weight;
	  Weight>0  edges集合中加入一个新的有向边,返回零

public boolean remove(L vertex)
思路:如果vertices中不包含该节点直接返回false,否则遍历edges删除所有包含该节	     点的有向边。

public Set<L> vertices()

思路:返回一个包含该图所有节点的集合即返回vertices的不可变形式拷贝
:

在这里插入图片描述

public Map<L, Integer> sources(L target)
思路:查找某节点的所有如入边:新建Map,遍历edges, 查找其中弧头与target相同的有向边,将边的弧尾与 权重加入Map,返回map。

public Map<L, Integer> targets(L source)
思路:查找某节点的所有如出边:新建Map,遍历edges, 查找其中弧尾与target相同的有向边,将边的弧头与 权重加入Map,返回map。

public String toString()
思路:空图返回:This is an empty Graph;否则显示图中所有有向边。

在这里插入图片描述

3.1.3.2 Implement ConcreteVerticesGraph
Vertrex类:
1、fields:
private final L name;
private final Map<L, Integer> sources = new HashMap<L, Integer>();
private final Map<L, Integer> targets = new HashMap<L, Integer>();
// Abstraction function:
// 以name节点姓名,targets入边集合,targets出边集合表示的抽象数据类型,构成到有向图点的映射
// Representation invariant:
//name不为空,且其各出边targets入边sources中value>0,不存在到自身的边,两点之间只存在一条边。
// Safety from rep exposure:
//将信息设置为private final,并添加getter方法,采取防御式编程2、private void checkRep()
不存在自身到自身的有向边,所有有向边的权重大于零。

在这里插入图片描述

3、constructor:

在这里插入图片描述

4、具体方法:

public L getName()
思路:返回该节点姓名标签

public Map<L, Integer> getSources()
思路:返回该顶点Vertex的 所有 入边 及 其权重。防御式编程新建一个map,将该顶点 source中所有元素加入新map,返回这个新的map。

public Map<L, Integer> getTargets()
思路:返回该顶点Vertex的所有出边及其权重。防御式编程新建一个map,将该顶点     source中所有元素加入新map,返回这个新的map。

public int setSources(L source, int weight)
思路:为该顶点添加,或删除,或更新某入边
如果入边集合sources中包含与该source标签相同的入边则删除该边;
Weight=0;返回已删除入边的权重weight;
Weight>0 根据输入信息在入边集合中加入一个新的入边,返回零

public int setTargets(L target, int weight)
思路:为该顶点添加,或删除,或更新某出边
如果出边集合targets中包含与该source标签相同的出边则删除该边;
Weight=0;返回已删除出边的权重weight;
Weight>0 根据输入信息在出边集合中加入一个新的出边,返回零
public String toString()
思路:将节点信息作为字符串输出

在这里插入图片描述

Vertex类的测试策略:

在这里插入图片描述

ConcreteVerticesGraph类的实现:
1、 fields:

private final List<Vertex<L>> vertices = new ArrayList<>();

// Abstraction function:
//用vertices点的集合抽象的图结构
// Representation invariant:
//vertices不存在重复的点
// Safety from rep exposure:

//属性用private final,使用防御式编程
2、private void checkRep()
思路:检查vertices中是否包含相同节点姓名的顶点。

在这里插入图片描述

2、 具体方法:
public boolean add(L vertex)
思路:先检查vertices中是否有与传入顶点姓名相同的节点,如果有返回false,否则根据传入姓名新建节点,并加入到图的顶点集合中,返回true。

public int set(L source, L target, int weight)
思路:首先建立两个标签flag1,flag2 .

如果顶点集合vertices中不包含 source 则flag1=-1 ;不包含 target 则flag2=-1;否则令flag为对应顶点在vertices中的位置索引。
如果该边的某顶点不存在则根据不存在顶点的信息新建一个节点并加入到图点集vertices中
在遍历vertices,分别调用vertex类的setTargets方法 setSources方法
设置某与source信息相同节点的出边和某target信息相同节点的入边

public boolean remove(L vertex)
思路:如果顶点集中不含含该信息节点返回false,
	 	否则vertices中删除与该节点信息相同的节点,在遍历vertices删除所有节点中与 

该节点连的边

public Set<L> vertices()
思路:返回一个新的包含包含所有节点姓名信息的集合

public Map<L, Integer> sources(L target)
思路:遍历vertices找到与target信息相同的顶点,调用vertex类的getSources()方法,
返回该顶点的入边。如果vertices不包含该点返回空map

public Map<L, Integer> targets(L source)
思路:遍历vertices找到与source信息相同的顶点,调用vertex类的getTargets()方
法,返回该顶点的入边。如果vertices不包含该点返回空map

public String toString()
思路:空图返回"This is an empty Graph"
否则返回图顶点信息表示的字符串

3.1.4 Problem 3: Implement generic Graph
3.1.4.1 Make the implementations generic

……

……

……

更新实现泛型,用占位符L修改对应String位置
3.1.4.2 Implement Graph.empty()
采用ConcreteEdgesGraph来实现Graph.empty()。

3.1.5 Problem 4: Poetic walks
3.1.5.1 Test GraphPoet
测试策略如下:

测试结果:

覆盖率结果:

测试辅助文件:

多行文件multiline,txt:

单行文件Single.txt

3.1.5.2 Implement GraphPoet
GraphPoet类:
1、field:
private final Graph graph = Graph.empty();

// Abstraction function:
//由对应文件生成的有向图
// Representation invariant:
//图其中的每个单词点不包含小写字母或数字外的其他字符
// Safety from rep exposure:

//属性采用private final标签

2、构造函数constructor:
public GraphPoet(File corpus) throws IOException
1、 先整行读取语料库文件,以空字符分割成字符串数组,再去除标点符号后,转换为
小写存入字符串列表

2、 再根据该字符串列表建图。相邻节点建立有向边权重设为一。

3、private void checkRep()
语料库构图后字符串不包含数字和小写字母外的其他字符。

3、 public String poem(String input)
思路:图为空图直接返回输入。
图非空,遍历input中所有单词,调用Graph.targets()和Graph.sources()方法,如果
该单词的targets和后面一个单词的sources有交集,找出交集中权重最大的新建一
个字符串bridge,并且在两个单词的中插入该字符串。输出最终结果

4、public String toString()
调用graph.toString方法将图转化为字符串输出。

3.1.5.3 Graph poetry slam
3.1.6 Before you’re done
请按照http://web.mit.edu/6.031/www/sp17/psets/ps2/#before_youre_done的说明,检查你的程序。
如何通过Git提交当前版本到GitHub上你的Lab2仓库。

创建本地仓库,添加远程仓库:
git init
git add *
git commit -m “提交”
git remote add origin https://github.com/ComputerScienceHIT/Lab2-1183300923.git
git push -u origin master

在这里给出你的项目的目录结构树状示意图。

3.2 Re-implement the Social Network in Lab1
在这里简要概述你对该任务的理解。
这个实验是基于在Poetic Walks中定义的Graph及其两种实现,重新实现Lab1中的 FriendshipGraph类。我们需要尽可能复用ConcreteEdgesGraph或 ConcreteVerticesGraph中已经实现的add()和set()方法,而不是从零开始。另外基于所选定的 ConcreteEdgesGraph 或 ConcreteVerticesGraph的rep来实现,而不能修改父类的rep。
3.2.1 FriendshipGraph类
给出你的设计和实现思路/过程/结果。
1、field
private final ConcreteEdgesGraph graph = new ConcreteEdgesGraph();

// Abstraction function:
//有向图构成的关系图
// Representation invariant:
//图中两点只有一条边,不存在自己到自己的边
// Safety from rep exposure:
//属性采用private  final标签

2、public void addVertex(Person person) throws IllegalArgumentException
思路:向关系图中添加节点,如果图中已包含该节点抛出异常提示重名。

3、、public void addEdge(Person p1, Person p2)
思路:调用图中set方法设置有向边

4、 public int getDistance(Person p1, Person p2)
思路:如果两节点名字标签形同返回0;
否则采用:广度搜索遍历建立队列将p1入队,将队首元素出队,将其未访问过的出边入队直到找到p2返回记录的距离。否则返回-1表示两点不连通。
用如下表示某节点是否被访问过,及到该点的最短距离。

5 public Set personGet()
思路:调用graph.vertices()返回该关系图中所有节点拷贝的集合。

6、public Map<Person,Integer> targetsGet(Person person)
思路:调用graph.targets(person)返回某节点所有出边
7、public Map<Person,Integer> sourcesGet(Person person)
思路:调用graph.sources(person)返回某节点的所有入边

程序运行结果:

3.2.2 Person类
给出你的设计和实现思路/过程/结果。
1、 filed
private final String name;

// Abstraction function:
//有标记的关系图节点
// Representation invariant:
//name不为空

// Safety from rep exposure:
//属性采用private final标签,并用get方法访问

2、构造函数

3、public String getName()
返回该节点的姓名

3.2.3 客户端main()
给出你的设计和实现思路/过程/结果。

按照实验手册实现如下程序:

在这里插入图片描述

3.2.4 测试用例
给出你的设计和实现思路/过程/结果。
测试策略如下图:

在这里插入图片描述

测试结过如下

测试覆盖度:

3.2.5 提交至Git仓库
如何通过Git提交当前版本到GitHub上你的Lab3仓库。
创建本地仓库,添加远程仓库:
git init
git add *
git commit -m “提交”
git remote add origin https://github.com/ComputerScienceHIT/Lab2-1183300923.git
git push -u origin master

在这里给出你的项目的目录结构树状示意图。

3.3 Playing Chess
3.3.1 ADT设计/实现方案
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
必要时请使用UML class diagram(请自学)描述你设计的各ADT间的关系。
1、Position类
1、Filed
private int x;
private int y;

	// Abstraction function:
	//x表示棋子横坐标,y表示纵坐标
	// Representation invariant:
	//x>=-1,y>=-1;
	// Safety from rep exposure:
	//属性用private final
x表示棋子横坐标,y表示纵坐标。(-1,-1)表示棋子未在棋盘上
2、constructor





3、public int getPositionX()
思路:返回位置横坐标

4、public int getPositionY()
思路:返回位置纵坐标

5、public void setPosition(int x, int y)
思路:更改位置的横纵坐标

6、@Override public String toString()
思路:返回位置信息如下格式:(x,y)

7、@Override public boolean equals(Object o)
思路:重写判相等方法。当横纵坐标都相等时即为相等

2、Piece类
1、Filed
private final String player;
private final String type;
private int state;
private final Position position = new Position(-1,-1);

	// Abstraction function:

//player表示棋子所属玩家,type表示棋子类型,state表示棋子状态,position表示棋子位置,组成棋盘上的棋子的抽象
// Representation invariant:
//player,type不为空;state等于0,-1或1;
// Safety from rep exposure:
//属性用private final
2、constructor

3、public String getPlayer()
思路:查看棋子所属玩家,返回玩家姓名的字符串。

4、public String getType()
思路:返回棋子类型国际象棋返回棋子种类,象棋返回棋子颜色。

5、public int getState()
思路:返回棋子状态,棋子在棋盘上返回1,未放置返回0,已移除棋

子返回-1
6、public Position getPosition()
思路:防御式编程:返回一个新的Position类的对象表示该棋子位置信
息。
7、public void setState(int state)
思路:设置当前棋子状态

8、public void setPosition(int x, int y)
思路:设置当前棋子坐标

3、Player类
1、field
private final String name;
private final Set pieces = new HashSet();
private final List historylist = new LinkedList();

// Abstraction function:
//name表示玩家姓名,pieces表示该玩家的棋子,history表示该玩家的下棋历史记录,共同表示手的抽象类型
// Representation invariant:
//name非空; 
// Safety from rep exposure:

//属性用private final标记,采用防御式编程返回mutable类型的拷贝

2、constructor
以传入玩家姓名构造Player类

3、public String getName()
思路:返回该玩家姓名标签

4、public Set<Piece> getPieces()
思路:返回该玩家所有棋子的拷贝集合

5、public List getHistory()
思路:获取该玩家下棋历史

6、public boolean addPiece(Piece piece)
思路:在该玩家的pieces 中加入一枚新棋子

7、public boolean removePiece(Position position)
思路:遍历 pieces 集合删除在传入位置坐标上的棋子

8、public boolean addHistory(String history)
思路:在该玩家的historylist中新增一条下棋记录

9、public int countQuantity()
思路:返回该玩家pieces  状态state为在棋盘上的棋子总数

4、Board类
1、field
private final int type;
private final int size;
private Piece[][] pieces;

	// Abstraction function:
//type表示棋盘类型,size表示棋盘大小,pieces表示棋盘各位置的棋子情况,共同表示棋局棋盘
	// Representation invariant:
	//type只能取0或1;size>0; 
	// Safety from rep exposure:
	//属性用private final

2、constructor

根据传入类型和大小构建棋盘,国际象棋棋盘大小为nn,围棋为n+1n+1。

3、public int getType()
思路:查看棋盘类型:国际象棋返回0;象棋返回1

4、public int getSize()
思路:返回棋盘大小size

5、public String findPosition(Position position)
思路:查看棋盘位置position处的所属
		如果该位置处存在棋子返回棋盘位置的所属者名字,无棋子返回

“null”,位置越界返回“false”,

6、public boolean checkBoundary(int x, int y)
思路:查看某位置是否越界

7、public boolean putPiece(Piece piece, Position position)
思路:在棋盘位置position处放置棋子piece,并检查位置是否越界,位

置是否被占用。

8、public boolean removePiece(Position position)
思路:移除棋盘位置position处的棋子,并检查位置是否越界,是否存在

该棋子。

9、public boolean movePiece(String player, Position position1, Position position2)
思路:将玩家player位置position1处的棋子移动到position2处,并检查是否越界,位置是否被占用,是否存在该棋子,该棋子是否归该玩家所有

在这里插入图片描述

10、public boolean eatPiece(String player, Position position1, Position position2)
思路:玩家player用自己位置position1处的棋子占据对方位置position2

出的棋子,并移除位置position2出的棋子与move类似检查是否
越界,位置是否被占用,是否存在该棋子,对应棋子是否归对应玩家所有

5、Action接口类
1、public static Action empty(String game)
根据输入的字符串判断以哪一个实例类作为默认的实现

2、public Board creatboard(Player player1, Player player2);
思路:新建初始化对应棋盘:

3、public boolean put(String player, Board board, Piece piece, Position positon);
思路:在棋盘board上执行玩家player的落子在某位置position的操作

4、public boolean remove(String player, Board board, Position positon);
思路:提子:某棋盘上移除某玩家player位置position处的棋子。

5、public boolean move(String player, Board board, Position position1, Position position2);
思路:移子:将某棋盘上某玩家的棋子从位置position1移动到位置

position2

6、public boolean eat(String player1,Board board, Position position1, Position position2);
思路:吃子操做:在某棋盘上将玩家player1位置position1处的棋子移

动到对手玩家的棋子位置position2处,并移除位置position2处的棋子
(1)ChessAction类:
因为国际象棋中没有落子和提子的行为,所以客户端并不允许调用placePiece和removePiece,这两个接口在实现时返回值永远为false。

在这里插入图片描述

国际象棋棋盘大小为8*8,开局没人16枚棋子类型反别为:2个Rook,2个kNight,2个Bishop,1个Queen,1个King,8个Pawn
2、public Board creatboard(Player player1, Player player2);
初始过程中依次建立两个玩家的棋子并放在对应位置上
3、因为国际象棋中没有落子和提子的行为,永远返回false

4、分别调用board的对应函数执行move和eat操作,并在board已有错误输入的检测

(2)GoAction类:
1、因为围棋中没有移子吃子的行为,所以客户端并不允许调用movePiece和eatPiece,这两个接口在实现时返回值永远为false。围棋棋盘大小为19*19

2、因为围棋中没有移子吃子的行为,永远返回false

3、分别调用board的对应函数执行put和remove操作,并在board已有错误输入的检测

6、Game类
1、field
private final Player player1;
private final Player player2;
private Board board;
private final Action action;

// Abstraction function:
//player1表示先手玩家,player2表示后手玩家,board表示棋局棋盘,action表示游戏操作
// Representation invariant:
//player1,player2,board,action不为空;player1不等于player2;
// Safety from rep exposure:
//属性用private final

2、constructor

创建对应游戏行为类,新建两个玩家
并调用action.creatboard()生成对应棋盘。

3、public String getPlayer1()
思路:返回游戏先手玩家姓名
4、public String getPlayer2()
	思路:返回游戏后手玩家姓名

5、public int getBoardSize()
思路:返回游戏棋盘的大小

6、public int getGameType()
思路:查看游戏类型 ,国际象棋返回0,象棋返回1;

7、public int countPieces(String player)
思路:如果玩家存在,返回玩家棋盘上的棋子数;否则返回-1;

8、public String findPosition(Position position)
思路:如果位置处存在棋子返回拥有者姓名,无棋子返回“null”,越界返回“false”。

9、public boolean pass(String player)
思路:跳过成功返回true,否则返回false。并在对应玩家中添加历史记录









10、public boolean put(String player, Position position)
思路:针对围棋:新建一个属于玩家player的棋子,落子在棋盘位置

position上,先判断先手还是后手玩家,先手新建一颗黑子,后手建立一颗白字。调用action.put执行落子操作,落子成功对应玩家添加落子记录

11、public boolean remove(String player, Position position)
思路:针对围棋:提子操作:将玩家player对手位置position上的棋子

移走。调用action.remove执行提子操作,提子成功对应玩家添加提子
记录。并删除对应玩家的对应棋子player1.removePiece(position);

12、public boolean move(String player,Position position1, Position position2)
思路:针对国际象棋:将某玩家player的棋子从位置position1移动到位

置position2。调用action.move执行移子操作,移动成功对应玩家添加
移子记录

13、public boolean eat(String player, Position position1, Position position2)

思路:针对国际象棋:游戏中一方player用某棋子占据对方棋子的位置,
并移走对方棋子。调用action.eat执行吃子操作,吃子成功对应玩家添加
吃子记录,并删除对应玩家对应棋子

14、public String getHistory()
思路:得到下棋过程中双方落子,提子,移子,吃子,跳过的记录,依次得到两玩家下棋历史,得到整局下棋过程。只记录下棋过程中双方落子,提子,移子,吃子,跳过的记录

3.3.2 主程序MyChessAndGoGame设计/实现方案
辅之以执行过程的截图,介绍主程序的设计和实现方案,特别是如何将用户在命令行输入的指令映射到各ADT的具体方法的执行。

在这里插入图片描述

首先让玩家输入游戏类型在根据输入类型建立对应游戏,在调用对应游戏执行过程的方法进行游戏

国际象棋的执行过程:先打印可执行操作再根据玩家输入执行具体的操作

围棋执行过程图如下所示:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值