HIT 软件构造Lab2引发对ADT的思考
HIT 软件构造Lab2引发对ADT的思考
抽象数据类型的特征主要体现在以下几个方面:
ADT就是数据抽象。用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。
数据封装。将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节,它包含两层含义:
①将数据和其行为结合在一起,形成一个不可分割的独立单位;
②信息隐藏,即尽可能隐藏数据内部细节,只留有限的对外接口形成一个边界,与外部发生联系。封装的原则使得软件错误能够局部化,大大降低排错的难度,便于软件的维护。 继承性。数据封装使得一个类型可以拥有一般类型的数据和行为,即对一般类型的继承。若特殊类型从多个一般类型中继承相关的数据和行为,则为多继承。多态性。多态性是指在一般类型中定义的数据或行为被特殊类型继承后,具有不同的数据类型或呈现出不同的行为。
例子是实验2中P3实现一盘棋类游戏:
1.最底层的类Piece:
final private String owner; //棋子拥有者 不可变
final private String type;//棋子类型 不可变
private int now; //现在的棋子的状态 0代表未放置 1代表已放置 2表示已删除
private Position nowposition; //现在棋子的位置
// Abstraction function:
// Piece代表棋盘上的棋子 ,type代表棋子的名称,now代表棋子的放置状态,owner表示持有者
// nowposition代表棋子的坐标
// Representation invariant:
// piece不能映射为空
//
// Safety from rep exposure:
// 所有fields都是 private
// 使用mutable数据类型
2.Player类:
final private String name;//玩家姓名 不可变
private Set pieces=new HashSet();//玩家的棋子集合
private List history=new ArrayList();//操作历史
// Abstraction function:
// Player映射操作pieces的玩家,name为玩家的名称,
// piece代表玩家的棋子,history的字符串为玩家历史
// Representation invariant:
// Player不能为空
//
// Safety from rep exposure:
// 所有fields都是 private
// 使用Collections.unmodifiableSet不可变类型
省略getter和setter方法
GetPiece:从玩家的棋子中选择没有放置在棋盘上的棋子,为了围棋设置
RemovePiece:从玩家的棋子集合中删除输入的棋子
Tostring:输出玩家的操作历史。
3board类:
final private int type;//规定棋盘的种类 1为国际象棋 2为围棋
final private int size;//定义棋盘的大小
private Piece[][] piece;
private int[][] place;
private int boardSize; //棋盘大小
private Piece[][] boardPosition; //棋子在棋盘的位置
//Abstraction function:
// Board 代表棋子所处的棋盘,boardSize代表了棋盘的大小,
// type代表棋盘类型
// boardPosition代表pieces在棋盘上的位置
// Representation invariant:
// Board不能映射为空
// Safety from rep exposure:
// 输出时进行防御性拷贝
// 所有fields都是 private
PutPiece:在制定位置放置棋子,如果位置已经占用或者超出棋盘大小则返回false
removePiece:在制定位置删除棋子,如果位置尚未占用或者超出棋盘大小则返回false
getplace:查询制定位置是否被占用,如果被占用则返回true 否则返回false;
4.position类:
就是表示位置,是一个二维坐标,重写equals函数来判断两个位置是否相等。方法就是简单的getter。
5.Action接口,这就很大程度的增大了代码的复用性,将两种棋类的共同特点进行整合:
/**
- 行为接口 具体实现在GOaction和INaction两个类里面
*/
public interface Action {
public static Action empty(String a) {
Action b =null;
if(a.equals("GO")) {
b = new GOaction();
}
else if(a.equals("IN")) {
b = new INaction();
}
return b;
}*
/**
* 创建一个新玩家1
*
* @param 一个字符串 代表玩家1的名称
* @return 一个以输入字符命名的玩家
*/
public Player createP1(String playerName1);
/**
* 创建一个新玩家2 不允许与1同名 在客户端判断
*
* @param 一个字符串 代表玩家2的名称
* @return 一个以输入字符命名的玩家
*/
public Player createP2(String playerName2);
/**
* 创建一个空棋盘
*/
public Board board();
/**
* 初始化棋盘
* 初始化多种棋子
* 为玩家分配对应的棋子
*/
public void create();
/**
*@param 棋手、初始位置和目的位置的横纵坐标
*将处于初始位置的棋子移动到目的位置。围棋专属
* 需要考虑处理各种异常情况,例如:
* 指定的位置超出棋盘的范围、目的地已有其他棋子、初始位置尚无可移动的棋子、两个位置相同、初始位置的棋子并非该棋手所有等,
* @return false
* 否则,将处于初始位置的棋子移动到目的位置,
*@return true。
*/
public boolean place(Player player,Piece piece,int x,int y);
/**
* @param 棋手、初始位置和目的位置的横纵坐标 将处于初始位置的棋子移动到目的位置。
* 需要考虑处理各种异常情况,例如:
* 指定的位置超出棋盘的范围、目的地已有其他棋子、初始位置尚无可移动的棋子、两个位置相同、初始位置的棋子并非该棋手所有等
* @return false
* 否则,将处于初始位置的棋子移动到目的位置
*@return true。
*/
public boolean move(Player player,int sx,int sy,int tx,int ty);
/**
* @param 棋手、一个位置的横纵坐标 将该位置上的对手棋子移除。
* 需要考虑处理异常情况,例如:该位置超出棋盘的范围、该位置无棋子可提、所提棋子不是对方棋子。围棋专属
* @return false。
* 否则,移除棋子
* @return true。
*/
public boolean remove(Player player,int x,int y);
/**
* @param 棋手、两个位置横纵坐标 将第一个位置上的棋子移动至第二个位置,第二个位置上原有的对手棋子从棋盘上移除。
* 需要处理异常情况,例如:
* 指定的位置超出棋盘的范围、第一个位置上无棋子、第二个位置上无棋子、两个位置相同、第一个位置上的棋子不是自己的棋子、第二个位置上的棋子不是对方棋子、等.
* @return false。
* 否则
* @return true。
*/
public boolean eat(Player player,int sx,int sy,int tx,int ty);
6.GOaction:
final protected static int size=19;//棋盘大小
final protected static int piecenum=361;//棋子数
protected Player player1;
protected Player player2;
protected Board GOboard;
// Rep invariants:
// p1和p2的用户名不许重复
// 棋盘类型是GO
// 棋子不得超过最大数量
// 棋子不许重复,棋盘上每一个位置的棋子是独一无二的
// Abstraction function:
// 代表着围棋的各种行为
// 两个玩家的棋子分布在棋盘上的点
// 函数将棋子放置位置和移除棋盘
// 玩家的棋子不执行吃和移动操作,只有落子和移子操作
createP1P2创建两个玩家
board():创建一个空的棋盘
public void create() //创建这局围棋游戏,初始化棋盘,分配棋子。
/**
* 放置棋子
* @param 执行操作的玩家,要操作的棋子,放置棋子的位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经创建并且操作的玩家和棋子的拥有者是对应的
* @return 操作执行成功返回true 否则返回false
*/
public boolean place(Player player,Piece piece,int x,int y)
/**
* 移除棋子
* @param 执行操作的玩家,移除棋子的位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经创建并且操作的玩家和棋子的拥有者是对立的
* @return 操作执行成功返回true 否则返回false
*/
public boolean remove(Player player,int x,int y)
7.Inaction
final protected static int size=8;//棋盘大小
final protected static int piecenum=32;//棋子数
protected Player player1;
protected Player player2;
protected Board INboard;
// Rep invariants:
// p1和p2的用户名不许重复
// 棋盘类型是IN
// 棋子不得超过最大数量
// 棋子不许重复,棋盘上每一个位置的棋子是独一无二的
// Abstraction function:
// 代表着国际象棋的各种行为
// 两个玩家的棋子分布在棋盘上的点
// 函数将棋子移动位置和吃
//玩家的棋子不执行落和移操作,只有移动和吃操作
createP1P2创建两个玩家
board():创建一个空的棋盘
public void create() //创建这局围棋游戏,初始化棋盘,分配棋子,将棋子放置在正确的位置上。
/**
* 移动棋子
* @param 执行操作的玩家,要移动棋子的位置,目标位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经在开始的位置,并且是自己的棋子,目标位置没有任何棋子
* @return 操作执行成功返回true 否则返回false
*/
public boolean move(Player player,int sx,int sy,int tx,int ty)
/**
* 吃棋子
* @param 执行操作的玩家,要移动棋子的位置,目标位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经在开始的位置,并且是自己的棋子,目标位置已经有敌方棋子
* @return 操作执行成功返回true 否则返回false
*/
public boolean eat(Player player,int sx,int sy,int tx,int ty)
8:Game类:
private Action action;
private Player p1;
private Player p2;
private Board board;
// Rep invariants:
// p1和p2的用户名不许重复
// 棋子不得超过最大数量
// 棋子不许重复,棋盘上每一个位置的棋子是独一无二的
// 代表这局游戏,根据选择的类型创建对应类型的游戏
// Abstraction function:
//代表这一局游戏
//代表着围棋的各种行为
// 两个玩家的棋子分布在棋盘上的点
// 函数将棋子放置位置和移除棋盘
// 象棋玩家的棋子执行吃和移动操作,围棋执行落子和移子操作
// Safety from rep exposure:
// 防御性拷贝
// 所有fields都是 private
/**
* 放置棋子
* @param 执行操作的玩家,放置棋子的位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经创建并且处于对应位置上并且操作的玩家和棋子的拥有者是对应的
* @return 操作执行成功返回true 否则返回false
*/
public boolean placePiece(String player,int x,int y)
/**
* 移动棋子
* @param 执行操作的玩家,要移动棋子的位置,目标位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经在开始的位置,并且是自己的棋子,目标位置没有任何棋子
* @return 操作执行成功返回true 否则返回false
/
public boolean movePiece(String player,int sx,int sy,int tx,int ty)
/*
* 移除棋子
* @param 执行操作的玩家,移除棋子的位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经创建并且操作的玩家和棋子的拥有者是对立的
* @return 操作执行成功返回true 否则返回false
/
public boolean removePiece(String player,int x,int y)
/*
* 吃棋子
* @param 执行操作的玩家,要移动棋子的位置,目标位置。位置参数不得为负数,不得超过棋盘大小,玩家必须已经创建,棋子必须已经在开始的位置,并且是自己的棋子,目标位置已经有敌方棋子
* @return 操作执行成功返回true 否则返回false
/
public boolean eatPiece(String player,int sx,int sy,int tx,int ty)
/*
* 查询某位置是否被占用 输出占用信息
* @param 查询的位置,目标位置。位置参数不得为负数,不得超过棋盘大小
* @return 操作执行成功返回true 否则返回false
/
public boolean check(int x,int y)
/*
* 查询某时刻棋盘上的所有棋子
* @param 玩家名称 必须已经创建
* @return 操作执行成功返回棋子数量 否则返回-1
*/
public int getNuberOfPiece(String player)
/**
* 查询某玩家进行的所有操作
* 输出历史记录字符串
* @param 玩家名称 必须已经创建
*/
public void getHistorySteps(String player)
设计了哪些ADT(接口、类),各自的rep和实现,各自的mutability/ immutability说明、AF、RI、safety from rep exposure。
必要时请使用UML class diagram(请自学)描述你设计的各ADT间的关系。
3.3.2 主程序MyChessAndGoGame设计/实现方案
键盘读取玩家选择游玩的游戏类型,和两位玩家的ID,不接受重复的id
根据输入的ID调用Game类里的方法创建一个新的游戏。
根据创建的游戏类型选择调用哪个act
把围棋的act和游戏集成在两个方法内,
public void startGoGame(Game game) 这个类就是围棋的游戏,用来根据用户的输入判断调用方法GOact中的哪个方法来进行围棋的操作。
GOact调用game类中写好的各种操作,让玩家通过菜单进行对应的操作。
双方交替进行行动,异常的行动不会产生任何影响。在读取到end时结束双方的行为,输出本场游戏的进程表。