Wednesday, August 26, 2015

Solving the Tire Pressure Monitoring System exercise (IV)

8. Achieving Dependency Inversion by extracting an interface.

Even though we are already injecting into Alarm the dependency on Sensor, we haven't inverted the dependency yet. Alarm still depends on a concrete implementation.

Now we'll show how to invert the dependency by extracting an interface.

Normally, it's better to defer this refactoring until you have more information, i.e., until the need for another sensor types arises, to avoid falling in the Speculative Generality code smell.

However, despite having only one type of sensor, we extract the interface anyway, as a demonstration of the refactoring technique.

So first, we rename the method that is being called on Sensor from Alarm, to make it less related with the concrete implementation of Sensor.

Then, following Kent Beck's guidelines in his Implementation Patterns book to name interface and classes, we renamed the Sensor class to TelemetryPressureSensor.

This renaming, on one hand, frees "Sensor" name so that we can use it to name the interface and, on the other hand, gives a more accurate name to the concrete implementation.

Then we extract the interface which is very easy relying on an IDE such as Eclipse or IntelliJ IDEA.

This is the generated interface:

This is Alarm's code and tests after removing any references to the pressure sensor from the naming:

Now we have a version of Alarm that is truly context independent.

We achieved context independence by inverting the dependency which makes Alarm depend on an abstraction (Sensor) instead of a concrete type (TelemetryPressureSensor) and also by moving the specificity of the SafetyRange configuration details towards its clients.

By programming to an interface we got to a loosely coupled design which now respects both DIP and OCP from SOLID.

In a dynamic language, we wouldn't have needed to extract an interface, thanks to duck typing. In that case Sensor would be a duck type and any object responding to the probe method would behave like a sensor. What we would have needed to do anyway, is the process of renaming the method called on sensor and eliminating any references to pressure from the names used inside Alarm, so that, in the end we have names that make sense for any type of sensor.

9. Using a builder for the Alarm.

Finally to make the Alarm tests a bit more readable we create a builder for the Alarm class.

That's all.

If you want to follow the whole refactoring process in more detail, I committed the code after every passing test and every refactoring. You can find the step by step commits here. The current version of the code is in this GitHub repo.

I hope this would be useful for the people who couldn't attend to the events (the SCBCN one or the Gran Canaria Ágil one)and also as a remainder for the people who could.

I'd also like to thank Luca Minudel for creating and sharing these great exercises and Álvaro García for pairing regularly with me and solved this exercise together.

This is the last post in a series of posts about solving the Tire Pressure Monitoring System exercise in Java:
  1. Solving the Tire Pressure Monitoring System exercise (I)
  2. Solving the Tire Pressure Monitoring System exercise (II)
  3. Solving the Tire Pressure Monitoring System exercise (III)
  4. Solving the Tire Pressure Monitoring System exercise (IV)

3 comments:

  1. Great article! I have one question though.

    After refactoring you now have a call to the class SafetyRange
    safetyRange.isNotWithin(value)

    Why isn't SafetyRange mocked in this example? I always thought you shouldn't call "real class" in a unit test... Just curious.



    ReplyDelete
    Replies
    1. Thanks!

      SafetyRange is not mocked because it's a value object.

      This post talks about things you shouldn't try to mock:
      Test Smell: Everything is mocked
      http://bit.ly/1QSs0PQ

      Delete