Ruby Checker实现:格式化复杂的多方法调用

时间:2011-12-06 21:41:25

标签: ruby methods refactoring

检查器需要的一个功能是防止玩家在可能跳过对方检查器的情况下移动检查器。为此,我有一个算法,如下所示:

  1. 检查单个检查器的相邻坐标位置(在2D阵列“板”上)
  2. 检查相邻位置的内容
  3. 如果相邻位置有内容,请检查是否是相反的检查员
  4. 如果是对方检查员,请检查是否有空地落地
  5. 列出单个检查员的潜在跳跃着陆点
  6. 调查整个董事会并列出所有潜在着陆点
  7. 当我尝试实施第6步时,我的问题就出现了。我发现自己必须包含一个辅助函数,如:

    def jump_location_finder_stack(board, x, y)
      jump_locations = {}
      opposing_checkers = opposing_checker_adjacent(determine_adjacent_positions_content(board,assign_adjacent_board_coords(x, y)))
      jump_locations = jump_locations(board, x, y, opposing_checkers)
      jump_locations
    end
    

    看到可笑的长对立的棋子分配(我本可以通过将其嵌入到jump_locations调用中使其更长)来调用整个“堆栈”的调用,因为每个调用都会从下一个调用开始。这是第六步方法:

    def generate_jump_locations_list(board, current_player)
      coordinates_list = []
      @current_player = current_player 
    
      board.each do |row|
        row.each do |loc|
          if (loc != nil) and (loc.color == @current_player)
            jump_locations = jump_location_finder_stack(board, loc.x_pos, loc.y_pos)
            coordinates_list << coordinates_of_jump_landings(loc.x_pos, loc.y_pos, jump_locations)  
          end
        end
      end
    
      coordinates_list.flatten
    end
    

    我可以在辅助方法中改进那个超长的“opposite_checkers”调用,或者换句话说,这种方法的长链接在代码中是“气味”吗?我已经包含了下面的完整列表,以防你好奇

    完整列表

    class BoardSurvey
    
      attr_accessor :board, :current_player
    
      QUADRANTS = ["upper_left", "upper_right", "lower_left", "lower_right"]
    
      def invert_array(array)
        array.map! { |x| -x }
      end
    
      def normal_deltas
        deltas = [1, 1, 1, -1, -1, 1, -1, -1] 
        @current_player == :red ? deltas : invert_array(deltas) 
      end
    
      def edge?(x)
        x > 7
      end
    
      def edge_adjust(hash)
        @current_player == :red ? hash.merge({"upper_left" => nil, "upper_right" => nil}) : hash.merge({"lower_left" => nil, "lower_right" => nil}) 
      end
    
      def deltas_to_board_locations(deltas, x, y)
        board_coords = []
        deltas.each_slice(2) do |slice|
          board_coords << x + slice[0] 
          board_coords << y + slice[1]
        end
        board_coords
      end    
    
      def assign_adjacent_board_coords(x, y) 
        jump_positions = Hash[QUADRANTS.zip(deltas_to_board_locations(normal_deltas, x, y).each_slice(2))]    
      end
    
      def determine_adjacent_positions_content(board, board_coords)
        adjacent_content = {}
        board_coords.each_pair { |quad, coords| adjacent_content[quad] = board[coords[0]][coords[1]] }
        adjacent_content
      end 
    
      def opposing_checker_adjacent(adjacent_content)
        opposing_checker_adjacent = {}
        adjacent_content.each_pair do |quad, content|
          if content != nil 
            content.color != @current_player ? opposing_checker_adjacent[quad] = true : opposing_checker_adjacent[quad] = false 
          else
            opposing_checker_adjacent[quad] = false
          end
        end
        opposing_checker_adjacent
      end
    
      def not_outside_bounds?(x, y, dx, dy)
        move_check = MoveCheck.new
        not move_check.out_of_bounds?(x + dx, y + dy)
      end
    
      def jump_possible?(board, x, y, deltas)
        (not_outside_bounds?(x, y, deltas[0], deltas[1]) and board[x + deltas[0]][y + deltas[1]] == nil) ? true : false
      end
    
      def delta_translator(quad, x, y, mag)
        deltas = []
        case quad
        when "upper_left"
          x += mag; y += mag
        when "upper_right"
          x += mag; y -= mag
        when "lower_left"
          x -= mag; y += mag
        when "lower_right"
          x -= mag; y -= mag  
        end
        deltas << x << y
        @current_player == :black ? deltas.reverse : deltas
      end
    
      def adjust_jump_locations_if_not_king(board, x, y, jump_locations)
        unless board[x][y].is_king?
          jump_locations["lower_left"]  = false
          jump_locations["lower_right"] = false
        end
        jump_locations
      end
    
      def jump_locations(board, x, y, opposing_checkers)
        jump_locations = {}
        opposing_checkers.each_pair do |quad, present|
          if present
            deltas = delta_translator(quad, x, y, 1)
            jump_possible?(board, x, y, deltas) ? jump_locations[quad] = true : jump_locations[quad] = false
          else
            jump_locations[quad] = false
          end
        end
        adjust_jump_locations_if_not_king(board, x, y, jump_locations)
        jump_locations
      end
    
      def coordinates_of_jump_landings(x, y, jump_locations)
        jump_coords = []
    
        jump_locations.each_pair do |quad, jump|
          if jump
            jump_coords << delta_translator(quad, x, y, 2)
          end
        end
        jump_coords
      end
    
      def jump_location_finder_stack(board, x, y)
        jump_locations = {}
        opposing_checkers = opposing_checker_adjacent(determine_adjacent_positions_content(board, assign_adjacent_board_coords(x, y)))
        jump_locations = jump_locations(board, x, y, opposing_checkers)
        jump_locations
      end
    
      def generate_jump_locations_list(board, current_player)
        coordinates_list = []
        @current_player = current_player 
    
        board.each do |row|
          row.each do |loc|
            if (loc != nil) and (loc.color == @current_player)
              jump_locations = jump_location_finder_stack(board, loc.x_pos, loc.y_pos)
              coordinates_list << coordinates_of_jump_landings(loc.x_pos, loc.y_pos, jump_locations)  
            end
          end
        end
        coordinates_list.flatten
      end
    end
    

1 个答案:

答案 0 :(得分:1)

长链方法通常被认为是一种不好的做法,因为它会降低代码的可读性。

首先,你可以在这里做的最简单的重构是'Extract Variable'。这将提高您方法的可读性:

def jump_location_finder_stack(board, x, y)
  adj_board_coords = assign_adjacent_board_coords(x, y)
  adj_position_content = determine_adjacent_positions_content(board, adj_board_coords)
  opposing_checkers = opposing_checker_adjacent(adj_position_content)
  jump_locations(board, x, y, opposing_checkers)
end

下一个链接方法的气味是你的班级BoardsSurvey太大而且做了很多不同的事情。这打破了Single Responsibility principle。你可以做的是应用'Extract Class'重构。在重构之后可以引入的可能类:Board,Location,PositionContext等。

启动后可能会有大量的重构工作,如果你不想破坏任何东西,我会建议你移动很小的步骤:确定代码气味,为你要重构的代码编写单元测试(如果现在没有测试),请进行小型重构。

希望这有帮助!