【4.12】软件构造Lab2(二)

3.3 Playing Chess
3.3.1 ADT设计/实现方案
一.Piece类
1.fields:
【4.12】软件构造Lab2(二)
Piece代表棋盘上的棋子 ,name代表棋子的名称,pieceState代表棋子的放置状态,pieceX代表棋子的横坐标,pieceY代表棋子的纵坐标

methods:
(1)所有fields的get函数和set函数。分别是返回Piece的fileds和通过传 递参数设置fields。
(2)将一个点从棋盘中移除。分别设置pieceState、pieceX、pieceY为 -1 。
【4.12】软件构造Lab2(二)
二、Position类
1.fields
【4.12】软件构造Lab2(二)
Position代表棋子在棋盘上的位置,x代表位置的横坐标,y代表位置的纵坐标
2.methods
(1)所有fields的get函数,分别返回该position 的横坐标和纵坐标。
(2)判断两个position是否相等。如果position的x、y坐标值都相等,则两个位置相等,返回true,否则返回false。

【4.12】软件构造Lab2(二)三. Player类
1.fields
【4.12】软件构造Lab2(二)
Player映射操作pieces的玩家,playerName为玩家的名称,remaining代表玩家在棋盘上的棋子,history的字符串为玩家历史
2.methods
(1)所有fields的get函数,返回Player的playerName,remaining,history
(2)playerName的set函数,根据传入的name设置this.playername
【4.12】软件构造Lab2(二)
(3)public Piece getPiece(String pieceName)
围棋go写的方法,根据传入的pieceName,从player的剩余棋子 remaining中返回一个未放置的棋子。如果没有,返回null。
【4.12】软件构造Lab2(二)
(4)public boolean addPieces(Piece piece)
如果该棋手remaining有这个棋子,则返回false;否则在remaining中添加该piece并且返回true。
【4.12】软件构造Lab2(二)
(5)public void addHistory(String oneHistory)
传入的参数是每个操作后需要添加的玩家历史,判断其为非空。再调用String.concat函数将传入的历史连接在this.history后面即可。
【4.12】软件构造Lab2(二)
(6)public boolean judgeOwnPiece(Piece piece)
参数是棋子piece,判断remaining中是否由该棋子,有返回true,否则返回false。
【4.12】软件构造Lab2(二)
(7)public int countQuantityOfPieceInBoard()
计算player在棋盘上的棋子总数,初始化sum = 0,遍历remaining,调用piece.getPieceState()方法,如果棋子状态为1,sum++,最后返回sum。

【4.12】软件构造Lab2(二)
四. Board类
1.fields
【4.12】软件构造Lab2(二)
Board 代表棋子所处的棋盘,boardSize代表了棋盘的边长,boardPosition代表pieces在棋盘上的位置
2.methods
(1)所有fields的get、set函数,分别返回和设置棋盘的边长、pieces在棋盘上的位置。
(2)检查输入数据大小的合理性,如果参数x、y的大小超出了棋盘范围,返回false,否则返回true。
【4.12】软件构造Lab2(二)
(3)获得指定坐标的棋子。如果参数坐标超出棋盘范围,抛出异常。
【4.12】软件构造Lab2(二)
(4)将棋子放置在指定的位置,如果参数x、y超出棋盘范围,或者该棋盘的此位置有棋子,则抛出异常。
【4.12】软件构造Lab2(二)
(5)改变指定位置棋子的pieceState。如果参数x、y超出棋盘范围,抛出异常。

【4.12】软件构造Lab2(二)
五. Action类
1.fields
【4.12】软件构造Lab2(二)
Action映射为棋手的动作,chessBoard是一个Board的对象
2.methods
(1)所有field的get和set函数。分别返回和设置this.chessBoard
(2)将棋子放置在指定的位置。先调用Board.getBoardPosition获得该position处的棋子,设置棋子的坐标,设置pieceState为1,将棋盘的position处棋子设置为piece。添加玩家历史。
【4.12】软件构造Lab2(二)
(3)将棋子移动到指定的位置。先获取oldPosition处的棋子,判断棋子归属正确,存在性等,new一个Piece对象piece,复制原棋子参数,将原位置棋子的pieceState设置为0,将newPosition处棋子设置为piece。添加玩家remaining,添加玩家操作历史。
【4.12】软件构造Lab2(二)
(4)先判断移除的是对方的且在棋盘上的棋子。将棋盘上对应的棋子pieceState设置为-1,调用Piece.remove,增加玩家历史。如果position超出棋盘的范围、position处无棋子可提、所提piece不是对方棋子,则抛出异常。
【4.12】软件构造Lab2(二)
(5)通过position1和position2获得要吃子piece1和被吃子piece2。判断棋子归属正确,存在性,将棋盘上position1处棋子pieceState设置为0,position2处pieceState设置为-1,并且在position2处新添加一个piece1的对象。移除piece2,添加newPiece到玩家的remaining中,添加玩家历史。
【4.12】软件构造Lab2(二)
六.Game类
1.fields
【4.12】软件构造Lab2(二)
Game代表了一局游戏,gameBoard映射为game中的棋盘,棋盘的操作者 是player1和player2,gameAction为棋手的操作
2.methods
(1)所有fields的get函数
(2)player1、player2、gameName的set函数
(3)以下四个方法调用Action类中的方法即可:
public void putPiece(Player player, Piece piece, Position position) throws Exception
public void movePiece(Player player, Position oldPosition, Position newPosition)
public void removePiece(Player player, Position position) throws Exception
public void eatPiece(Player player, Position position1, Position position2) throws Exception
(4)在指定的position处获得一个棋子。调用Board.getBoardPiece()即可。
【4.12】软件构造Lab2(二)
(5)设置国际象棋chess棋盘。设置棋盘的gameName,通过参数设置players名称,初始化棋盘,设置32个pieces的pieceName、pieceState、坐标为(-1,-1)。将棋子添加到棋盘上,增加玩家棋子

(6)设置围棋go棋盘。设置棋盘的gameName,通过参数设置players名称,初始化棋盘,增加玩家棋子,设置 pieces的pieceName、pieceState、坐标为(-1,-1)。最后将棋子添加到棋盘上。
【4.12】软件构造Lab2(二)
(7)打印棋盘。遍历gameBoard即可
【4.12】软件构造Lab2(二)
3.3.2 主程序MyChessAndGoGame设计/实现方案
设计方案:
1.fields:
【4.12】软件构造Lab2(二)
2. public void myMain() throws Exception:
【4.12】软件构造Lab2(二)
3. public void StartChessGame(Game game) throws Exception
首先打印chess能进行的操作menu,初始化后利用swtich语句进行相应的 acton
【4.12】软件构造Lab2(二)【4.12】软件构造Lab2(二)
4. public void StartGoGame(Game game) throws Exception
思路与StartChessGame函数相类似。
【4.12】软件构造Lab2(二)【4.12】软件构造Lab2(二)
测试覆盖率结果:

【4.12】软件构造Lab2(二)

实验过程中收获的经验和教训:
(1) 面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?
ADT比较抽象,对初学者会有一定难度,但掌握后就会好很多
(2) 使用泛型和不使用泛型的编程,对你来说有何差异?
泛型编程应用更广泛
(3) 在给出ADT的规约后就开始编写测试用例,优势是什么?
优势在于可以在设计的同时进行测试,测试模块化的功能是否实现,有助于编程的正确性检查。
(4) P1设计的ADT在多个应用场景下使用,这种复用带来什么好处?
可以节省很多时间和精力,免去大量重复性代码的书写
(5) 为ADT撰写specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?
意义在于明确自己设计的ADT的接口和使用规范,方便复用ADT,以后编程中也会试着写