Yesterday I had to implement a common, simple bit of functionality in a view. The view is revealed and hidden by clicking a tab button which causes it to slide up or down behind a mask.
It’s not rocket science. The only awkward aspect of the implementation is that it has to be responsive to the user changing their mind mid-move. If I click the tab and then quickly change my mind, the movement should be reversed, and if I click it again it should be reversed again and so on.
This is the kind of behaviour I’ve implemented hundreds of times over the years, and you probably have too. I do it without really thinking about it. Except that these days I’m trying to think more about every line of code I write – inspired by some stuff I read (and now can’t find) where Corey Haines was talking about challenging yourself to answer the “Why stop now?” question when refactoring.
Refactoring the way I refactor
John Lindquist pointed out on my previous post about Multiple Developer-Personality Disorder that I take things too seriously. He’s right – it’s my state of being – I’m a nerd exactly as defined by Rands in Response:
A nerd needs a project because a nerd builds stuff. All the time. Those lulls in the conversation over dinner? That’s the nerd working on his project in his head.
So – my current project appears to be a new level of deconstruction of how I form and change habits. How I integrate new practices – how I get better at whatever I’m learning, whether that’s Zuma’s Revenge Blitz on Facebook or writing cleaner code. (Observing how you learn is a fantastic excuse for playing computer games by the way.)
When you’re trying to change a habit you can set yourself a big, public, motivational goal, “I’m entering the marathon!”, or you can try to become aware of discrete, seemingly insignificant opportunities to sneak the new behaviour in until it takes.
I’m particularly interested in the later approach, and how we move through awareness to intent, then conscious experimentation until we start to integrate the new behaviour.
So – indulge my current nerd obsession with refactoring-refactoring for a moment.
A simple case of state-specific behaviour
We have a panel, with a button that moves the panel up or down. If the panel is up, or going up, it should go down, and if the panel is down, or going down, it should move up. Depending on which state it’s in – up, going up, down, going down – the required behaviour changes.
There were couple of ifs in that sentence, and a conditional is probably the most obvious approach to solving this problem:
Ok, that works. But I’m trying to refactor conditionals out of my code any time it’s possible.
So, my first refactoring goal was to get rid of that Boolean and conditional. I went for a decoupled-by-variable handler approach:
I think that’s better. Each time a movement is executed it changes the reference for the next movement. If we imagine that we’re in a less binary situation than a panel that moves up/down, this could be extended to add extra phases to the movement by adding a new handler and tweaking the references.
And then, presumably because I’ve been geeking-out on the “What are the lesser known but cool data structures?” StackOverthrow thread, I had a moment of wondering how I might use a strongly typed Linked List (not the mx.utils variety, just a pure implementation of the pattern) to implement this.
currentItem.nextto get the next item.
Linked Lists have some specific performance benefits over Arrays – such as splicing out dead items faster – but in this implementation what I’m drawn to is the clarity of intent.
I created a LinkedListPoint class: [edit - This class is poorly named - if you're not familiar with Linked Lists, "LinkedAnimationPoint" would reduce confusion here.]
And then I refactored the panel movement to use this class:
Of the three, I like this implementation best (for now). I think the actual toggle behaviour is easiest to understand. The creation function is only run once, and describes the implementation. I’m not interested in the more/less lines of code metric, but I am very interested in the (future coder’s) WTF/minute metric.
“How many lines of code do I need to read to have a complete enough picture of this in my head to do whatever I need to do?”
It’s also simpler to extend or vary, for the situation where the movement is not just up/down and you want to add additional steps.
I created a new Class, but this is a one-time effort and now it’s in my library. Whether LinkedListPoint is the correct name for it I’m not sure. Maybe it’s not describing the right thing. Fine if you know what a Linked List is, but if you don’t then it could be confusing. [edit: So far the comment consensus is that "LinkedAnimationPoint" would be an improvement.]
Of course the benefit is not this trivial implementation. Any of the three approaches is sufficient. The interesting part is that I learned something – I realised that a simple implementation of the Linked List pattern could be used to implement a lightweight state-management pattern.
A Bonsai-State-Machine, if you like.
If you have further solutions to offer, shout them out.
What a pointless refactor – just git-r-done dude!
As I understand it, the goal of refactoring isn’t purely to polish your code base, it’s also to give yourself opportunities to experience different approaches, compare the experiences and learn something from that comparison. Concrete learning is very different from abstract learning – implementing a solution gives you a different quality of learning to just reading about a potential solution.
We’re often attracted to the sexy refactoring problems: “Better framework”, “Better blitting engine”, “Better event system” – even “Better compiler” – problems. These are the things I see people drawn to refactoring, even when they can’t see the point in smaller refactors.
But one of the things I’ve learned this last year or so, through having the opportunity to swap commits and comments with the people who are my AS3 heroes – Shaun Smith, Joel Hooks, Till Schneidereit, Robert Penner, Joa Ebert – is that the people people who deliver the big refactors (robotlegs, swift-suspenders, as3-signals, apparat) are consistently concerned with the little refactors too.
These folk will debate an explicit vs coerced null check. They sweat the way the code is laid out - ‘this line is too long’ – so that you never have to scroll sideways in your editor. They take a dozen shots at the perfect name for an API function. They worry about the optimal order of the parameters. They iterate over error messages.
No piece of code is too small to be refactored. But not in blind pursuit of smaller/faster/less – in creating more usable, more useful, code that is a joy to work with.
What I’ve come to believe is that you can’t get to be the person who comes up with as3-signals without being the person who learned tens of thousands of tiny but useful lessons from “pointless” refactors first.
So I’ll keep on with my little pointless refactors, water my Bonsai-State-Machine with a few more trial implementations, and hope that one day I grow up to be like one of my AS3 heroes.