国际象棋程序中的简洁与简洁

时间:2018-02-13 02:19:41

标签: java refactoring

我已经阅读了有关代码气味,代码重复等的多篇文章/堆栈交换问题,我仍然在决定如何处理我的问题时遇到一些麻烦。我正在写一个简单的国际象棋程序,它有一个名为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结构,我觉得我会牺牲可读性来减少重复。在您看来,如果它提高了可读性并降低了代码的复杂性,是否允许重复使用两个较小的方法,或者减少重复总是更好?

2 个答案:

答案 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);
    }
}