将经典代码更改为功能代码

Changing classic code to functional

我有以下 丑陋 (但有效)代码的一部分,用于检查棋盘上的字段是否为空:

if (abs(xDst - xSrc) == abs(yDst - ySrc)) {
            boolean backslashMove = xSrc < xDst && ySrc > yDst || xSrc > xDst && ySrc < yDst;
            if (backslashMove) {
                int y = max(ySrc, yDst) - 1;
                for (int x = min(xSrc, xDst) + 1; x < max(xSrc, xDst); x++) {

                    if (board.getActiveChessmanAt(x, y).isAlive()) {
                        return false;
                    }
                    y--;
                }
            } else { //slash move

显然,它会检查类似 Bishop 的移动线中坐标 (xScr, ySrc) 和 (xDst, yDst) 之间的字段。

我正在尝试使用 IntStream 对此进行转换:

if (backslashMove) {
                final int y = max(ySrc, yDst) - 1;

                if (IntStream.range(min(xSrc, xDst) + 1, max(xSrc, xDst))
                        .anyMatch(x -> board.getActiveChessmanAt(x, y).isAlive()))
                return false;

在这种情况下,我该如何执行 y-- ?如果要在 'anyMatch' 命令

中使用,它必须是最终的

如果你真的需要使用流重写它,那么你可以利用xy同时递增的事实。所以你可以构建一个增量的范围而不是x-values的范围:

final int xSrc = min(xSrc, xDst) + 1;
final int xDst = max(xSrc, xDst);
final int ySrc = max(ySrc, yDst) - 1;

if (IntStream.range(0, xDst - xSrc)
    .anyMatch(distance -> board.getActiveChessmanAt(xSrc + distance, ySrc + distance).isAlive())) {
    return false;
}

一般来说, 不可能直接使用 "parent" 方法中的 non-final 局部变量。 Java 不支持 真正的闭包 。为此,您需要一个包装器对象(AtomicInteger 是一个经常被推荐的候选对象),或者您可以将 non-final 变量设为一个 class 字段(注意潜在的线程安全问题)。就我个人而言,这两个 "tricks" 都不好。

您需要的不是 streams/folds 方面的函数式编程。
相反,您应该重构您的实际代码以使其成为 clearer/shorter/better。

例如,您可以:

  • 把散落在实际方法中的逻辑部分提取到具体方法中有意义的名字

  • 使用结构化对象而不是太精细的单一变量

  • 删除不需要的嵌套:使用提前退出和不需要的条件语句可能会有所帮助

它可以给:

// -> extract method + structured objects
if (!isPointsMatch(pointSrc, pointDst)) {
    return false; // -> early exit
}

// -> extract method + structured objects
if (isBackslashMove(pointSrc, pointDst)) {
     if (board.hasAnyActiveChessmanAlive(pointSrc, pointDst)) {
          return false;  
      }
}
// else slash move -> The else is useless
// ...    

您截取的原始代码是程序。您的功能方法效果不佳。那么 面向对象 方法怎么样?

class Position{
  private final int x, y;
  public Position(int x, int y){
     this.x=x;
     this.y=y;
  }
  public getX(){
    return x;
  }
  public getY(){
    return y;
  }
}

interface Move {
  Position moveFrom(Position start);
}

interface Figure {
  Collection<Move> getPossibleMoves(Position start, Board board);
}

class Bishop implements Figure {
  private final Collection<Move> moves = new HashSet<>();
  public Bishop(){
      moves.add(start->new Position(start.getX()-2,start.getY()-1));
      moves.add(start->new Position(start.getX()-2,start.getY()+1));
      moves.add(start->new Position(start.getX()+2,start.getY()-1));
      moves.add(start->new Position(start.getX()+2,start.getY()+1));
      moves.add(start->new Position(start.getX()-1,start.getY()-2));
      moves.add(start->new Position(start.getX()-1,start.getY()+2));
      moves.add(start->new Position(start.getX()+1,start.getY()-2));
      moves.add(start->new Position(start.getX()+1,start.getY()+2));
  }

  @Override
  public Collection<Move> getPossibleMoves(Position start, Board board){
    return moves.stream()
                .filter({ Position end = m.moveFrom(start);
                          return board.isOnBorad(end.getX(),end.getY())
                               && board.getActiveChessmanAt(end.getX(), end.getY()).isAlive()})
                .collect(Collectors.toSet());
  }
}

Figure 的另一个实现可能 return 每个步骤的单独 Move 实例,直到达到限制:

class Tower implements Figure {
   enum Direction {
      NORTH(1,0),EAST(0,1),SOUTH(-1,0),WEST(0,-1);
      private final Position change;
      private Direction(int x, int y){
         change = new Position(x, y);
      }
      public Position getNextFrom(Position start){
          return new Position(start.getX()+change.getX(),start.getX()+change.getY());
      }

      @Override
      public Collection<Move> getPossibleMoves(Position start, Board board){
         Collection<Move> moves = new HashSet<>();
         for(Direction direction : Direction.values()){
           Position current = direction.getNextFrom(start);
           while( board.isOnBorad(current.getX(),current.getY())
               && board.getActiveChessmanAt(current.getX(), current.getY()).isAlive()){
             moves.add(p-> new Position(current.getX(),current.getY());
           }
        }
        return moves;
      }
}