In Step 1, we created a superclass to hold the common logic for all flexible layouts. Now, let’s change the model from inheritance to composition. My goal in this step was to move all DragTile functionality into a separate helper class, leaving DragTile as just an empty shell.
Until now, we have not had to worry about the contract between the container and the layout manager. All we did in step 1 was decide which methods belonged in the superclass and the subclass on a somewhat haphazard basis.
Now that we want to move this into a separate helper object, we need to ask ourselves how the main object (the container) needs to communicate with the helper object. A good starting point is to look at the members that the DragTile subclass defines.
All of the properties (green dots) seem to be specific to DragTile. Meanwhile, three member functions at the bottom look truly general: measure(), findItemAt(), and generateLayout(). Let’s use these as our initial interface for ILayout.
Because measure() is a method defined on IUIComponent, It doesn’t make sense to just move it to a non-UIComponent object. Instead, I decided to define a helper function, getMeasuredSize(), that we call from the measure() function. And thinking through some of the other complexities that might arise, I added an attach() and detach() method so that the layout manager could do some initialization and cleanup whenever it was used. The interface now looks like this:
I now had to make this “real” by creating a layout property of FlexibleContainer and creating a concrete implementation of ILayout called TileLayout.
How do we get from where we are in step 1 to where we need to get to in step 2? In step 1, DragTile is a subclass of FlexibleContainer. After step 2, we know that there won’t be a class called “DragTile” anymore.
This seems like a hard change to make incrementally, and the whole point of refactoring is to do these changes in small, incremental steps. Which brings me to the next tip.
Tip 5: If needed, build temporary scaffolding to make sure your code continues to “work” as you refactor.
In this case, I used both inheritance and delegation while I was in the middle of the refactoring. In other words, I wrote a little bit of scaffolding code to keep the DragTile class around even after I had created the TileLayout class, and slowly moved code over from one to the other.
For those of you who are not familiar with the ideas behind refactoring, this writing of temporary code might seem kind of insane. However, once you get used to it, you will never go back. Big changes are much easier to do successfully in small, bite-sized pieces. Sometimes this means writing a few lines of shim code to hold things up as you change things incrementally.
The next step was to move all the functionality out of the DragTile subclass. During this phase, I didn’t move entire methods. Instead, I dissected methods line by line and moved the correct functionality over to the appropriate class using the following simple rules:
- If the logic contained in the line of code was “general”, it move it to the FlexibleContainer superclass
- If the logic contained in the line is specific to tiles, move it to the “TileLayout” class
This process worked fine for most of the logic, but the styles were a different matter. TileLayout is not a UIComponent, so it doesn’t participate in the CSS system. Meanwhile, the styles that DragTile defines are specific to tiles (vGap, hGap, etc). While it is possible to have things that are not UIComponents participate in CSS, it takes a lot of work, and I didn’t feel like tackling it right then.
What I decided to do was to turn off the style functionality temporarily. This brings us to the next tip:
Tip 6: When temporarily disabling functionality during a big refactor, be sure to do so in a way that preserves information, ideally through stub functions.
One way to turn off the CSS functionality would have been to comment out the lines of code that refer to CSS and restore them later. The problem with this approach is that it is difficult to make sure that the the code inside your comments continues to be “correct” as you move your code around.
To help make sure that the CSS knowledge that was embedded in Ely’s code didn’t get lost, I created a stub getStyle() function that always returned NaN. This meant that all of the CSS logic in Ely’s code was still there and still functioning, but it was attached to a CSS style sheet where nothing was ever defined.
By keeping Ely’s code alive, it was easy to make sure that I was making the right edits to it as I refactored. As I moved code around, the compiler would remind me if I was doing something terribly wrong. This doesn’t prevent all coding errors, but it helps prevent many of them.
After all this, I was able to move all of the code from the DragTile code into one of the other two classes. The final version of the DragTile class looked like this:
public class DragTile extends FlexibleContainer
public function DragTile()
layout = new TileLayout();
We are now (obviously) at the point where we can get rid of the DragTile object. Code for step 2 can be found here