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.