Saturday, November 6, 2010

Replace method with method object

This technique I found very useful, and needed when untangling complex functions, with local variable dependencies. I believe that when a function becomes too complex, or too large, it is often a hint that there should be another class here, taking care of business in a more systematic way. At first it seems like a big deal to untangle the complex funcion, but as you start doing it, you usually realize how much cleaner the logic becomes when it's all separated into the other class. 
  1. The original function becomes less cluttered, as it offloads the work to the new class
  2. The new class becomes cleaner, for the same reason, but often even more than expected, because the functionality offloaded to the new class can help clean out other functions in the original class
  3. Once the logic has been dumped into the new class, it may be clearer how to simplify it further, and reduce the coupling between the original and the new class
This is not a trivial process, so I'll refer to Martin Fowler's book for the mechanics (p.136):
  • Create a new class, name it after the method
  • Give the new class a final field for the object that hosted the original mehtod (the source object) and a field for each temporary variable and each parameter in the method
  • Give the new class a constructor that takes the source object and each parameter
  • Give the new class a method named "compute"
  • Copy the body of the original method into compute. Use the source object field for any invocations of methods on the original object [my italics]
  • Compile
  • Replace the old method with one that creates the new object and calls compute
  • Now comes the fun part. Because all the local variables are now fields, you can freely decompose the method without having to pass any parameters
Now notice the italicized text. This can be an important glitch, which I'm sure Fowler is aware of but didn't want to get into for the sake of simplicity. Often you'll find that in your attempt to disentagle a function in this fashion, you end up creating 2 classes which are entangled with each other. The new class will (at first) need to make calls to functions of the original class. Of course it would be a shame to leave it there. Often if your new class ends up calling functions of the original class is a signal that perhaps some more functions should be offloaded to the new class, or some new parameters need to be passed in to the functions of the new class. There is an art to this, and at this point I don't really have a checkpoint list of how that situation should be handled, as it comes on a case by case basis. Sorry, no examples here, as last time I did this type of refactoring was a few months ago. 

No comments:

Post a Comment