Looking for information about composed refactorings
Even after all these years, I take too-big steps while coding. In less finicky languages (or finicky languages with good tool support), I can get away with it, but I can’t do that in C++ (a language I’ve been working in the past two weeks). So I’ve had to think more explicitly about taking baby steps. And that’s made me realize that there must be some refactoring literature out there that I’ve overlooked or forgotten. That literature would talk about composing small-scale refactorings into medium-scale changes. The only example I can think of is Joshua Kerievsky’s Refactoring to Patterns. I like that book a lot, but it’s about certain types of medium-scale refactorings (those that result in classic design patterns), and there are other, more mundane sorts.
Let me give an example. This past Friday, I was pairing with Edward Monical-Vuylsteke on what amounts to a desktop GUI app. Call the class in question Controller
, as it’s roughly a Controller in the Model-View-Controller pattern. Part of what Controller
did was observe what I’ll call a ValueTweaker
UI widget. When the user asked for a change to the value (that is, tweaked some bit of UI), the Controller
would observe that, cogitate about it a bit, and then perhaps tell an object deeper in the system to make the corresponding change in some hardware somewhere.
That was the state of things at the end of Story A. Story B asked us to add a second ValueTweaker
to let the user change a different hardware value (of the same type as the first, but reachable via a wildly different low-level interface). The change from “one” to “two” was a pretty strong hint that the Controller
should be split into three objects of two classes:
-
A
ValueController
that would handle only the task of mediating between aValueTweaker
on the UI and the hardware value described in story A. -
A second
ValueController
that would do the same job for story B’s new value. The difference between the twoValueControllers
wouldn’t be in their code but in which lower-level objects they’d be connected to. -
A simplified
Controller
that would continue to do whatever else the originalController
had done.
The question is: how to split one class into two? The way we did it was to follow these steps (if I remember right):
-
Create an empty
ValueController
class with an emptyValueController
test suite. HaveController
be its subclass. All theController
tests still pass. -
One by one, move the
Controller
tests that are about controlling values up into theValueController
test suite. Move the code to make them pass. -
When that’s done, we still have a
Controller
object that does everything it used to do. We also have an end-to-end test that checks that a tweak of the UI propagates down into the system to make a change and also propagates back up to the UI to show that the change happened. -
Change the end-to-end test so that it no longer refers to
Controller
but only toValueController
. -
Change the object that wires up this whole subsystem so that it (1) creates both a
Controller
andValueController
object, (2) connects theValueController
to theValueTweaker
and to the appropriate object down toward the hardware, and (3) continues to connect theController
to the objects that don’t have anything to do with changing the tweakable value. Confirm (manually) that both tweaking and the remainingController
end-to-end behaviors work. -
Since it no longer uses its superclass’s behavior, change
Controller
so that it’s no longer a subclass ofValueController
. -
Change the wire-up-the-whole-subsystem object so that it also makes story B’s vertical slice (new
ValueTweaker
, newValueController
, new connection to the hardware). Confirm manually.
I consider that a medium-scale refactoring. It’s not something that even a smart IDE can do for you in one safe step, but it’s still something that (even in C++!) can easily be finished in less than an afternoon.
So: where are such medium-scale refactorings documented?
February 5th, 2012 at 2:43 am
I think the refactoring you described refers to Kerievsky’s refactoring where you change a class to a strategy object, as the different ValueControllers seem to be just about that. I was wuite certain that Josh had this in his book, but it seems that it isn’t. In that case I think you’re right, such a source would be really helpful at a lot of clients.
February 20th, 2012 at 3:06 pm
[…] on (mostly twitter) response to my note asking for information on composed refactorings, I’ve concluded there’s a gap in the knowledge-market, and I’ve decided to see if […]