Reversing Dependency Direction

Dependency direction is reversed when object_a depends on object_b but the code is refactored, so object_b depends on object_a. The goal is to always have objects depend on objects that are less likely to change.

class HockeyTeam
  def players_names
    # players is an Array of player objects
    players.map do |player|
      player.full_name
    end
  end
end

The players_names method depends on the Array#map method of the Ruby core library to function. In this case, we can be confident that our dependency direction is right because HockeyTeam is going to be more variable than the Array class in the Ruby core library. The HockeyTeam class is following the golden rule of only depending on objects that are less variable than self.

Suppose we are creating an application to measure the compatibility between two people. The Calculator class is responsible for defining a multiply method and the Compatibility class is responsible for scoring two people. After two people are scored, the #secret_sauce method multiples the results for the overall compatibility match between the two people. To start, the #secret_sauce method is in the Calculator class:

class Calculator
  def multiply(x, y)
    x * y
  end

  def secret_sauce(compatibility)
    # if Compatibility changes, this method may break
    multiply(compatibility.person_x_score, compatibility.person_y_score)
  end
end

class Compatibility
  def person_x_score
    # a value is hardcoded here, but pretend that this is a result of complicated math
    5.33
  end

  def person_y_score
    2.34
  end
end

Calculator depends on Compatibility to compute the #secret_sauce result. This code organization is not optimal because Calculator has standard math utility functions and is unlikely to change. Compatibility has the logic for a complex matching algorithm that is likely to change frequently, so Compatibility should depend on Calculator. The following refactoring illustrates how the dependency can be reversed:

class Calculator
  def multiply(x, y)
    x * y
  end
end

class Compatibility
  def person_x_score
    5.33
  end

  def person_y_score
    2.34
  end
 
  def secret_sauce
    # if Calculator changes, this method may break, but is unlikely Calculator will change
    Calculator.new.multiply(person_x_score, person_y_score)
  end
end

Dependencies are inevitable and managing dependency direction is critical. Follow the golden rule of always depending on objects that are less variable than self will decrease the probability that changes will break the application.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s