Data Duplication

It is easy to see code duplication and eliminate it during the refactoring step. However, it is difficult to see the data duplication in code that could be a sign of hidden abstraction. We must reflect about data duplication and consider expressing the hidden domain concept in a class during the refactoring step. You might also accomplish this by aiming for low semantic gap in the solution. The language we use to describe a problem affects the code quality.

I discuss this in depth in my upcoming TDD in Ruby book scheduled for release in March 2017 by Apress. In this book I review the solution by Anderson Dias for Conway's Game of Life found at: https://github.com/andersondias/conway-game-of-life-ruby. The cell object has a neighbors method that looks like this:

class Cell
  def neighbours
    neighbours = []
    neighbours.push(@world.cell_at(self.x - 1, self.y - 1))
    neighbours.push(@world.cell_at(self.x - 1, self.y))
    neighbours.push(@world.cell_at(self.x - 1, self.y + 1))

    neighbours.push(@world.cell_at(self.x, self.y - 1))
    neighbours.push(@world.cell_at(self.x, self.y + 1))

    neighbours.push(@world.cell_at(self.x + 1, self.y - 1))
    neighbours.push(@world.cell_at(self.x + 1, self.y))
    neighbours.push(@world.cell_at(self.x + 1, self.y + 1))

    neighbours
  end

  def live_neighbours
    self.neighbours.select do |n|
        n && n.live?
    end
  end
end

In this case, you cannot replace the hard coded 1 with a constant and call it a day. This solution suffers from Primitive Obsession, since the location is accessed using integer values. The domain specific concept, Location is missing. The x and y stores data and is duplicated. What does x and y represent? We need to capture that abstraction and hide the data in x and y within the class. My solution uses Location object that looks like this:

class Location
  NORTHWEST = [-1, 1]
  NORTHEAST = [1, 1]
  SOUTHWEST = [-1, -1]
  SOUTHEAST = [1, -1]
  CENTER    = [0, 0]
  NORTH     = [0, 1]
  SOUTH     = [0, -1]
  EAST      = [1, 0]
  WEST      = [-1, 0]

  OFFSETS = [NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST, NORTH, SOUTH, EAST, WEST]

  def self.add(first, second)
    [first[0]+second[0], first[1]+second[1]]
  end
end

The combination of a specific pair of x and y represents a specific location in the Moore's neighborhood. You can also see the Location class provides a closed operation, the add method.

Where it fits, define an operation whose return type is the same as the type of its arguments. Such an operation is closed under the set of instances of that type. A closed operation provides a high-level interface without introducing any dependency on other concepts. This pattern is most often applied to the operations of a Value Object.
-- Eric Evans, Domain Driven Design

The location class now provides us a place to allocate related responsibilities. I discuss these concepts in more depth in the book. Subscribe to my newsletter if you want to be notified about the discount coupon codes for the upcoming book.


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.