Wednesday, 17 December 2014

12 Tips of Christmas - 7 Inversion of Control

12 Tips of Christmas - 7 Inversion of Control

A lot of my posts in this series tend to revolve around a common theme, refactoring. When extracting a class, or method, you should look to inversion of control to make testing, and identification of dependencies easier.
Suppose we had a class like this:
class Receipt
  def mark_purchase
    @purchase = Purchase.latest.first
    enter_purchase_amount @purchase.amount
  end
end
By returning an instance of purchase within the method, we have created an implicit dependency on Purchase, this also makes it harder to test the method without first requiring an instance of the Purchase model. On top of this, we are now tied to the Purchase API, when all we wanted is something that would respond to amount.
Fixing this is easy.
class Receipt
  attr_reader :purchase

  def initialize purchase
    @purchase = purchase
  end

  def mark_purchase
    enter_purchase_amount purchase.amount
  end
end
Now when we instantiate our object, we can give it an instance of any object, so long as that object responds to the amount method, giving us some great flexibility
On top of that, our class can now be decoupled from ActiveRecord within our tests, as all we need to do is provide a test double for a purchase object.
One other benefit is that now, our dependency on purchase is explicit, this assists with future maintainers as they will be able to quickly identify it rather than get surprised down the road.
Of course this example is overtly simple, in our object if we didn't require any other reference to purchase, it would be far simpler to just pass in our amount as a dependency. This example, does however illustrate that by moving the responsibility of assigning the dependency up the chain, we've gained an interface thats more flexible, easier to maintain and easier to test.

No comments:

Post a Comment