LinkedList Bonsai-State-Machine through Pointless Refactoring

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.

The view is behind a mask, clicking the button reveals the view

The view is behind a mask, clicking the button reveals the view, clicking again hides it.

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.

A Boolean is, by definition, inflexible. It is or it isn’t. If you want a third or fourth state you have to introduce a second and maybe third Boolean. And then things get irritating pretty quickly.

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.

In a Linked List, each item holds a reference to the next item. Instead of looping through an array, we have a reference to the current item, and we simply do currentItem.next to 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.

WTFs-per-minute often boils down to:
“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 SchneidereitRobert 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.

Brilliant programmers value the learning that comes with each small improvement, and with each screw up; these people screw up too, they’re just really good at reversing decisions when they turn out to be wrong.

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.

About the Author

I'm an actionscript programmer living and working in a tiny village in the Yorkshire Dales, UK. I used to be a TV reporter, but my inner (and often outer) geek won. I also write stuff. Most recently Head First 2D Geometry.

Visit Stray's Website

Share the post

Delicious It Digg this! Stumble this! Share on Reddit Share on Buzz Share on FriendFeed
  • http://twitter.com/psyked_james James Ford

    Holy moley. I understand the concept, but the code itself – that’s beyond my WTF / minute-o-meter. I’m sure it works great though!

    • http://www.xxcoder.net Stray

      That’s interesting – can you bear to tell me whether there was a specific line that spiked your WFT?

      I wonder whether renaming the LinkedListPoint – say “ConsecutiveAnimatedPoint” or something – would have made a difference?

  • http://twitter.com/dan_cake Daniel Goldsworthy

    Great post!
    Now to check the rest of your blog for gems like this one
    Cheers

    • http://www.xxcoder.net Stray

      Thanks Daniel – I hope you found the post about complexity as that’s the most similar. But boy it’s nice to have people read-around :)

  • http://troygilbert.com/ Troy Gilbert

    Stray, I effing love the way your brain works. Brilliant refactoring. And I love the name, Bonsai State Machine!

    • http://www.xxcoder.net Stray

      That’s the best comment ever. Thanks – and yes, I think Mr Penner’s ‘Mario Mushroom Operator’ might have tweaked something in my internal dictionary configuration.

  • http://www.xxcoder.net Stray

    I’ve now spotted an extra refactor – I could refactor the _currentTargetPosition reference to a private getter method, which would trigger createPositionTargets first time round so that this creation code is lazy.

    This way, if the user never clicks the tab, the LinkedListPoint objects are never instantiated.

    • http://squeedee.tumblr.com/ Rasheed Abdul-Aziz

      Now that I’ve seen so many well executed examples of fluent interfaces, i would certainly try replacing your LLP constructor and .next property with fluent methods. (I say try, it may make it worse..) – but then you might have:

      _currentTargetPosition = new LinkedListPoint(0, OFF_STAGE_Y)
      .createNext(0,ON_STAGE_Y)
      .makeCircular()

      or

      _currentTargetPosition = new LinkedListPoint()
      .add(0, OFF_STAGE_Y)
      .add(0,ON_STAGE_Y)
      .makeCircular()

      or something like that. You will spend a lot of time tinkering until you get the fluent contexts the way you like them, but you can create really elegant languages with them

      • http://www.xxcoder.net Stray

        That is really lovely.

        I guess you’d need a carry the back reference as well so you can make the loop, but it would be great. I’m also loving Fluent Builders as I get more and more exposed to them. Nice idea.

        • http://squeedee.tumblr.com/ Rasheed Abdul-Aziz

          I kinda thought everything would be in the context of the first LLP. Making .makeCircular a little slow in that it would run ‘findLast’, but have all the information it needs?

  • http://squeedee.tumblr.com/ Rasheed Abdul-Aziz

    I love this kind of thing, but I never truly have the time to work like this. I’d love to, but I’ve not yet been fully joined by my collegues. Still working on them though, I have hope.

    • http://www.xxcoder.net Stray

      Keep the faith man, keep the faith. Or lift a device popular in soap-operas – mess with their heads until they think they’re losing it and then replace each team member one at a time with someone from the light-side.

      • http://squeedee.tumblr.com/ Rasheed Abdul-Aziz

        Thats called constructive dismissal in our country. It isn’t legal :P

  • http://twitter.com/tanya Tanya Gray

    I’ll admit I got slightly lost at the LinkedListPoint only because the concept was new to me. Made sense after re-reading, but perhaps a re-name is in order. Even a simple change like “LinkedAnimationPoint” would probably help, from my point of view.

    • http://www.xxcoder.net Stray

      I added some annotations – you’re right, LinkedListPoint is rubbish :)

      It’s also very useful to know that the Linked List concept is new to you. I’m working on some early content ideas for a possible new book on working with big data, and one of the questions I’m tossing around with my editor and co-author is where to begin – my feeling is that most active developers only know the data structures they’ve used so far.

      • http://twitter.com/tanya Tanya Gray

        We work with relatively big data at my workplace and although I had heard of linked lists in other languages I had never used or encountered it in use in actionscript – AS3 being my first and only programming language so far.

        I think you are right though, most developers only know what they have used, and my feeling is that the majority of them don’t usually seek out new and better ways to do things. Most will just hack and twist basic concepts to fit more complex projects.

        First rule of big data: don’t use XML ;)

        Definitely sounds like the kind of book I’d be interested in – will keep an eye on your progress :)

  • http://twitter.com/Daganev Daganev

    I find the nextMotion() framework to be easier to read and understand than the linkedList set up as well.

    However, I think the linkedList as a Bonsai-State-Machine is a great idea and with a few helper functions to create new states, insert new states between previously defined points etc, it could be a great util class for people to have.

    • http://www.xxcoder.net Stray

      That’s a really nice suggestion – and maybe those extra functions could be classes themselves, rather than expand the actual Point. Like new LinkedPointInsertBetween(A, B, C) so that the actual implementation is still really skinny?

    • http://www.xxcoder.net Stray

      That’s a really nice suggestion – and maybe those extra functions could be classes themselves, rather than expand the actual Point. Like new LinkedPointInsertBetween(A, B, C) so that the actual implementation is still really skinny?

  • Anonymous

    As the class has no idea it’s being used for “animation” I don’t think that’s a proper part of its name.

    • http://twitter.com/Daganev Daganev

      I think PointStateMachine or PointBonsaiState or something of that sort, might make it more clear. However the part that confused me the most was how it “loops” that part took me a few re-reads to get.

    • http://www.xxcoder.net Stray

      You’re quite correct! Hmm… names are always tricky aren’t they? Thank god my kid was 12 and already had one when I became his step-mum.

  • http://twitter.com/mikeysee Michael Cann

    Great post Stray!

    I agree with virtually everything you said in there.

    To learn to be a better coder you have to get out of your comfort zone of using the same solution to the same problem over and over again, you have to ask yourself: is there a better way of doing this? if not why not?

    I find the most brilliant coders (definitely agree with the list you made at the end there) come up to solutions to problems I didn’t even know existed!

    • http://www.xxcoder.net Stray

      “… solutions to problems I didn’t even know existed!”

      That is such a powerful observation Mike! I think you’ve just inspired my next blog post – it’s about the learning-cycle stuff we talked about, but I needed another thought to pull it all together. And there it is! Nice one mate :)

  • http://twitter.com/nelsonHalifax nelson halifax

    exciting!
    but why do you set null in the constructor of onPosition and then on the third line onPosition.next = offPosition, why not var onPosition:LinkedListPoint = new LinkedListPoint(0, ON_STAGE_Y, offPosition) ?

    • http://www.xxcoder.net Stray

      That’s the trouble with LinkedLists that are circular – the last position doesn’t exist when the first position is created, so you have to append it afterwards.

  • http://twitter.com/KrasimirTsonev Krasimir Tsonev

    Stray, I think that you are really close to these AS3 heroes. Really helpful article, keep going with such kind of things ;)

    • http://www.xxcoder.net Stray

      Thanks Krasimir – I think I still have a long way to go but it’s very nice to be on the journey!