我已经阅读了有关代码气味,代码重复等的多篇文章/堆栈交换问题,我仍然在决定如何处理我的问题时遇到一些麻烦。我正在写一个简单的国际象棋程序,它有一个名为Piece的抽象类。现在,Piece类有两个非常相似的方法,addRank和addFile:
/**
* Adds squares that constitute legal moves in the same rank as a piece to an ArrayList
* Used in Rook, King, and Queen implementations of getAvailableMoves()
* @param availableMoves The ArrayList that the squares will be added to
* @param startIndex The starting index for additions (for Rook, Queen, this
* will be the beginning of the board; for Kings it will be one space to
* their left
* @param endIndex The ending index for additions (for Rook, Queen, this
* will be the end of the board; for Kings it will be one space to
* their right
*/
public void addRank(ArrayList<Square> availableMoves, int startIndex, int endIndex) {
int currX = this.square.getXCoord();
int currY = this.square.getYCoord();
for(int i = currX + 1; i < endIndex; i++) {
if(Game.board[i][currY].getHasPiece()) {
if(hasOppositePiece(this, Game.board[i][currY])){ //capturing a piece of a different color constitutes a legal move
availableMoves.add(Game.board[i][currY]);
}
break; //Rooks, Queens, King cannot move past another piece
}
availableMoves.add(Game.board[i][currY]);
}
for(int i = currX - 1; i >= startIndex; i--) {
if(Game.board[i][currY].getHasPiece()) {
if(hasOppositePiece(this, Game.board[i][currY])){
availableMoves.add(Game.board[i][currY]);
}
break;
}
availableMoves.add(Game.board[i][currY]);
}
}
/**
* Adds squares that constitute legal moves in the same file as a piece to an ArrayList
* Used in Rook, King, and Queen implementations of getAvailableMoves()
* @param availableMoves The ArrayList that the squares will be added to
* @param startIndex The starting index for additions (for Rook, Queen, this
* will be the beginning of the board; for Kings it will be one space
* above them
* @param endIndex The ending index for additions (for Rook, Queen, this
* will be the end of the board; for Kings it will be one space
* below them
*/
public void addFile(ArrayList<Square> availableMoves, int startIndex, int endIndex) {
int currX = this.square.getXCoord();
int currY = this.square.getYCoord();
for(int i = currY + 1; i < endIndex; i++) {
if(Game.board[currX][i].getHasPiece()) {
if(hasOppositePiece(this, Game.board[currX][i])){
availableMoves.add(Game.board[currX][i]);
}
break;
}
availableMoves.add(Game.board[currX][i]);
}
for(int i = currY - 1; i >= startIndex; i--) {
if(Game.board[currX][i].getHasPiece()) {
if(hasOppositePiece(this, Game.board[currX][i])){
availableMoves.add(Game.board[currX][i]);
}
break;
}
availableMoves.add(Game.board[currX][i]);
}
}
现在,显然这两种方法几乎相同,我可以将它们合并为一种方法。但是,这将涉及向我的函数添加更多参数,以及更复杂的if / else结构,我觉得我会牺牲可读性来减少重复。在您看来,如果它提高了可读性并降低了代码的复杂性,是否允许重复使用两个较小的方法,或者减少重复总是更好?
答案 0 :(得分:0)
您可能希望抽象出碎片的行为并进一步移动。类似的东西:
interface Piece {
Stream<Move> getSingleMoves();
boolean allowsMultipleMoves();
Position apply(Move move);
}
interface Move {
Position apply(Position position);
Move add(Move other);
}
然后你的逻辑将成为:
List<Move> moves = piece.getSingleMoves()
.flatMap(move -> multipleMoves(piece, move))
.collect(Collectors.toList());
private Stream<Move> multipleMoves(Piece piece, Move move) {
if (piece.allowsMultipleMoves()) {
return Stream.iterate(move, m -> board.isOpen(piece.apply(m)), Move::add);
} else if (board.isOpen(piece.apply(move)) {
return Stream.of(move);
}
}
这可能不完全正确(我已经忽略了反对派并离开了董事会)但它可能会让你知道如何进一步抽象并避免重复。您的算法不应该真正了解电路板的实现细节,例如将数据存储在数组中。理想情况下,它应该能够充分了解件,位置和板,以产生合法的移动,而不必遍历索引。
答案 1 :(得分:0)
它显然非常主观,但考虑到四个非常相似的循环,我会说它值得重构。一种方法是将循环体移动到另一个方法,当它碰到一块时返回false:
public void addRank(ArrayList<Square> availableMoves, int startIndex, int endIndex) {
int currX = this.square.getXCoord();
int currY = this.square.getYCoord();
for(int i = currX + 1;
i < endIndex && addMove(availableMoves, Game.board[i][currY]);
i++);
for(int i = currX - 1;
i >= startIndex && addMove(availableMoves, Game.board[i][currY]);
i--);
}
public void addFile(ArrayList<Square> availableMoves, int startIndex, int endIndex) {
int currX = this.square.getXCoord();
int currY = this.square.getYCoord();
for(int i = currY + 1;
i < endIndex && addMove(availableMoves, Game.board[currX][i]);
i++);
for(int i = currY - 1;
i >= startIndex && addMove(availableMoves, Game.board[currX][i]);
i--);
}
private boolean addMove(List<Square> availableMoves, Square square) {
if(square.getHasPiece()) {
if(hasOppositePiece(this, square)) {
availableMoves.add(square);
}
return false;
}
availableMoves.add(square);
return true;
}
另一个选择是将迭代逻辑本身传递给另一个方法。您可以使用流来在Java 9中执行此操作:
public void addRank(ArrayList<Square> availableMoves, int startIndex, int endIndex) {
int currX = this.square.getXCoord();
int currY = this.square.getYCoord();
addMoves(availableMoves,
IntStream.iterate(currX + 1, i -> i < endIndex, i -> i + 1)
.mapToObj(i -> Game.board[i][currY]));
addMoves(availableMoves,
IntStream.iterate(currX - 1, i -> i >= startIndex, i -> i - 1)
.mapToObj(i -> Game.board[i][currY]));
}
public void addFile(ArrayList<Square> availableMoves, int startIndex, int endIndex) {
int currX = this.square.getXCoord();
int currY = this.square.getYCoord();
addMoves(availableMoves,
IntStream.iterate(currY + 1, i -> i < endIndex, i -> i + 1)
.mapToObj(i -> Game.board[currX][i]));
addMoves(availableMoves,
IntStream.iterate(currY - 1, i -> i >= startIndex, i -> i - 1)
.mapToObj(i -> Game.board[currX][i]));
}
private void addMoves(List<Square> availableMoves, Stream<Square> squares) {
for(Iterator<Square> iter = squares.iterator(); iter.hasNext(); ) {
Square square = iter.next();
if(square.getHasPiece()) {
if(hasOppositePiece(this, square)){
availableMoves.add(square);
}
break;
}
availableMoves.add(square);
}
}