refactoring Control Parameter

1.7k Views Asked by At

I'm using a tool for finding code smells in code called reek and I have a problem with one called Control Parameter

def place_ship(ship, start_position, orientation)
    @row = start_position[:row]
    @column = start_position[:column]
    ship.length.times do
        if orientation == :vertical
            vertical_place_ship(row,column,ship)
        else
            horizontal_place_ship(row,column,ship)
        end
    end
end

def vertical_place_ship(row,column,ship)
    self.grid[row][column].ship = ship
    self.grid[row][column].status = :occupied
    @row += 1 
end

def horizontal_place_ship(row,column,ship)
    self.grid[row][column].ship = ship
    self.grid[row][column].status = :occupied
    @column += 1
end

warning's content: [

55]:ControlParameter: Board#place_ship is controlled by argument 'orientation

How do I fix this?

2

There are 2 best solutions below

2
On

Regardless of the tool feedback, looking at your code, the only line that differ between the horizontal & vertical case is whether to increase @rows or @columns. An option could be:

def place_ship(ship, start_position, orientation)
    row = start_position[:row]
    column = start_position[:column]
    ship.length.times do
        self.grid[row][column].ship = ship
        self.grid[row][column].status = :occupied
        orientation == :vertical ? row += 1 : column += 1
    end
end

I removed the two (identical) methods, and just used the ternary operator ('?') to increase the correct variable after placing each ship part.

0
On

'Orientation' is a flag value in place_ship method. Value of 'orientation' is not changing as the code executes. So no need to check it 'ship.length' times.

place_ship has only conditional logic and nothing else. This is unnecessary and the conditional logic can reside outside. You are passing in a flag, that tells the method what path to choose, conditionally. This is the Conditional Coupling smell. Generally do not pass in a conditional parameter to a method. Have 2 different methods for the 2 choices and name them aptly.

You already have aptly named vertical_place_ship and horizontal_place_ship methods. You can refactor it like this.

def <method_that_calls_place_ship>
// other code
    if orientation == :vertical
      vertical_place_ship(ship, start_position)
    else
      horizontal_place_ship(ship, start_position)
    end
// more code
end

def vertical_place_ship(ship, start_position)
    row = start_position[:row]
    column = start_position[:column]

    ship.length.times do
      self.grid[row][column].ship = ship
      self.grid[row][column].status = :occupied
      row += 1 
    end  
end

Similarly for horizontal_place_ship method.