Refactoring for Separation Of Concerns: A real world example

time to read 2 min | 400 words

I just did a major refactoring to Rhino DSL, to add orthongality to the DslEngine. It was an interesting refactoring, and something that is worth talking about.

Let us take a look at the before image, first:


We have two classes that are involved in creating and managing DSL. Notice the FileFormat, CreateInput, GetMatchingUrlsIn and NotifyOnChange methods on the DslEngine? Or the GetFromCache, RemoveFromCache, SetInCache methods?

The firsts are related to the storage of the DSL scripts on disk and the second batch are related to caching, they have nothing to do with the actual work performed by the DslEngine. Those are completely different responsibilities and concerns. I didn't feel the pain of mixing the responsibilities until I had to create a class that wanted to change the storage mechanism of the scripts.  Looking at it, the cache methods especially really want to move to another class. You can tell by their names.

Then I realized that I had to override a bunch of methods and correlate results between them, and I found myself thinking, "Who the hell wrote this nightmare?" *.

Changing the storage mechanism, something that I certainly wanted to support, has gotten fairly involved, and I couldn't see anyone figuring out all the knobs they had to turn without reading the source, carefully. This is a bad situation to be in.

Hence, refactoring.

What was involved in this was mostly moving code around. The technicalities of it where mostly these:

  1. Ask R# to extract super class with the unrelated methods.
  2. Ask R# to extract an interface from the new super class
  3. Remove the inheritance association with the DslEngine
  4. Add a property for the new interface type
  5. Initialize the property to use a default implementation
  6. Fix code that broke as a result (mostly involved adding the property name before the method).
  7. Rename methods to match current location (Removing the "Cache" from the methods in the caching interface).

The result is this:


Now, changing the storage mechanism doesn't need to affect the DSL implementation. We have a more robust model, and a good example for the blog.

Of course, I just finished writing half a chapter about the old way of doing things... Time for a rewrite.

* I often think thoughts like this, the sad answer is often: "Me".