Design pattern – Behaviors

ActionScript

Design pattern - BehaviorsI recently started working at MediaMonks where they are using this interesting pattern of coding; behaviors as a class. I started playing with ‘behaviors’ myself to see how this could work for me. If you use this pattern once, you’ll wonder why you did not use it ever before 😉 This post will lead you through some examples and shows some benefits of using it. This post is written with focus on actionscript 3 developers, who can read code and knows already something about object oriented programming. The goal is to learn more and become a better developer.

What is a behavior?

Well I guess everybody knows what it means, but in terms of code I would say that a behavior-class is some kind of a simple controller with a target. It does one thing, it controls the target with a specific feature/goal. The idea is to add functionality by assigning a behavior to an object (composition), instead of extending it to get the functionality (inheritance). That sound complicated, so why not check out what it practically means?

Difference between inheritance and composition

Let’s say you want to program an object that follows your mouse. How would you create it? I would create a class named MouseFollowingSprite which has all functions to do that. Then I would extend or create an instance of that MouseFollowingSprite-class and BANG I’m done 🙂

Typical example of inheritance

class MouseFollowingSprite extends Sprite

Okay, lets write down that killer class:

package nl.stroep.display
{
import flash.display.Sprite;
import flash.events.Event;

/**
* @author Mark Knol
*/
public class MouseFollowingSprite extends Sprite
{
private var _ease:Number;

public function MouseFollowingSprite()
{
init();
}

private function init():void
{
this.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}

private function handleEnterFrame(e:Event):void
{
update();
}

private function update():void
{
this.x += (this.mouseX – this.x) / _ease;
this.y += (this.mouseY – this.y) / _ease;
}
}
}

(You see that? It’s nicely done with some eased motion :D)

I have never seen a problem with extending, but I had troubles with finding a good way to add other kind of functions to this type of classes. In terms of OOP, is a rule to write classes with a single responsibility, and we already passed that one. But imaging you want your mouse following sprite also to scale when you scroll with your mouse wheel. Well that would be easy: Just create a class named ScrollWheelScalingSprite, which extends MouseFollowingSprite. Now it has both functionality, when you would create an instance of it.

Let’s create our ScrollWheelScalingSprite, it would like this:

package nl.stroep.display
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.MouseEvent;

/**
* …
* @author Mark Knol
*/
public class ScrollwheelScaleSprite extends MouseFollowingSprite
{
private var _ease:Number;
private var _targetScale:Number;
private var _hasListener:Boolean;
private var _deltaRatio:Number = 0.1;
private var _minimum:Number = NaN;
private var _maximum:Number = NaN;

public function ScrollwheelScaleSprite()
{
_targetScale = this.scaleX;

init();
}

private function init():void
{
stage.addEventListener(MouseEvent.MOUSE_WHEEL, handleMouseWheel);
addEnterFrameListener();
}

private function handleMouseWheel(e:MouseEvent):void
{
_targetScale += e.delta * _deltaRatio;

if (!isNaN(_minimum) && _targetScale < _minimum) _targetScale = _minimum; else if (!isNaN(_maximum) &&_targetScale > _maximum) _targetScale = _maximum;

addEnterFrameListener();
}

private function addEnterFrameListener():void
{
if (!_hasListener)
{
this.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
_hasListener = true;
}
}

private function removeEnterFrameListener():void
{
this.removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
_hasListener = false;
}

private function handleEnterFrame(e:Event):void
{
update();
}

private function update():void
{
this.scaleX += (_targetScale – this.scaleX ) / _ease;
this.scaleY = this.scaleX;

if (Math.abs(this.scaleX – _targetScale) < .01) { removeEnterFrameListener(); } } } } [/as] Let's go some deeper. What if you want a ScrollwheelScaleSprite which should not follow our mouse? That would be a problem, because it already has that functionality. However you could hack around around by adding some protected booleans, to enable the actual functions of the class. Name them like _mouseFollowingEnabled. When your class extend some other classes, you’ll probably need a _scrollWheelScalingEnabled too. In the constructor of the new class you just turn on/off the booleans and your done. But then again, if you want to use this to Bitmap, you have a problem, you cannot extend a Sprite and a Bitmap at the same time.

I mean, this is how we code, right? All classes have one responsibility, but could this example been done better? While writing, this post reminds me in a way to this older post of Keith Peters; you should read it. It also has a interesting discussion.

Create behaviors

As already stated, a behavior class is a controller. It deals with an object (we call it target) to add the actual behavior. Now I don’t think extending is bad, but I think we should use it with care.

Let’s create an abstract behavior class, which can be extended. (This is not needed, but very helpful for accessing the target and prevent duplicate code)
[as]
package nl.stroep.behaviors
{
import flash.display.DisplayObject;

/**
* @author Mark Knol
*/
public class AbstractBehavior
{
protected var _target: DisplayObject;

public function AbstractBehavior(target: DisplayObject)
{
this._target = target;
}
}
}

I think this does not require lots of explanation. We have a class, it saves a target
that is passed from the constructor. We can do lot’s of stuff inside a behavior from this point.

Now, let’s convert our MouseFollowingSprite to a MouseFollowingBehavior, which should extends the AbstractBehavior. Most drastic change is to convert ‘this‘, to ‘_target‘ and change the constructor to make it an actual behavior:

package nl.stroep.behaviors
{
import flash.display.DisplayObject;
import flash.events.Event;

/**
* @author Mark Knol
*/
public class MouseFollowBehavior extends AbstractBehavior
{
private var _ease:Number;

public function MouseFollowBehavior(target: DisplayObject, ease:Number = 5)
{
super(target);

_ease = ease;

init();
}

private function init():void
{
_target.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}

private function handleEnterFrame(e:Event):void
{
update();
}

private function update():void
{
_target.x += (_target.mouseX – _target.x) / _ease;
_target.y += (_target.mouseY – _target.y) / _ease;
}
}
}

This is not magic, but it is very powerful. We can assign this behavior to a Sprite, but also to a Bitmap or Video etc. This would not that easy / possible with inheritance only. The class works standalone and we don’t need booleans to enable or disable the behavior.

Ok, let’s also create the scroll wheel scale behavior:

package nl.stroep.art.behaviors
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.MouseEvent;

/**
* …
* @author Mark Knol
*/
public class ScrollwheelScaleBehavior extends AbstractBehavior
{
private var _ease:Number;
private var _targetScale:Number;
private var _hasListener:Boolean;
private var _deltaRatio:Number;
private var _minimum:Number;
private var _maximum:Number;

public function ScrollwheelScaleBehavior(target: DisplayObject, ease:Number = 5, deltaRatio:Number = 0.1, minimum:Number = NaN, maximum:Number = NaN)
{
super(target);

_ease = ease;
_deltaRatio = deltaRatio;
_minimum = minimum;
_maximum = maximum;
_targetScale = target.scaleX;

init();
}

private function init():void
{
_target.stage.addEventListener(MouseEvent.MOUSE_WHEEL, handleMouseWheel);
addEnterFrameListener();
}

private function handleMouseWheel(e:MouseEvent):void
{
_targetScale += e.delta * _deltaRatio;

if (!isNaN(_minimum) && _targetScale < _minimum) _targetScale = _minimum; else if (!isNaN(_maximum) &&_targetScale > _maximum) _targetScale = _maximum;

addEnterFrameListener();
}

private function addEnterFrameListener():void
{
if (!_hasListener)
{
_target.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
_hasListener = true;
}
}

private function removeEnterFrameListener():void
{
_target.removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
_hasListener = false;
}

private function handleEnterFrame(e:Event):void
{
update();
}

private function update():void
{
_target.scaleX += (_targetScale – _target.scaleX ) / _ease;
_target.scaleY = _target.scaleX;

if (Math.abs(_target.scaleX – _targetScale) < .01) { removeEnterFrameListener(); } } } } [/as] Now we can assign a behavior very easy: Example of applying the behaviors

var sprite:Sprite = new Sprite();
new MouseFollowBehavior(sprite);
new ScrollwheelScaleBehavior(sprite);

You are now free to add the behaviors you’ll need, and leave the one you don’t need. It’s like creating little plugins. Fun eh? It’s nice to add parameters to the constructor, to make it less static. The other fun part is that if you build a behavior right, you could copy/paste it to another project or in you library, because it has no dependencies to classes you don’t want or need. Of course, this is not something new, you could have seen this in temple-lib but also the Hype-framework too, and maybe lots of other framework probably use this pattern in a way.

Now we know the difference:
Inheritence: “I want a Sprite that follows my mouse”
Behaviors: “I want something that makes a Sprite follow my mouse”

Killing the behaviors

The code examples in this post does not have any function to remove it, but if you want to remove a behavior, it would be wise to assign the behavior to a variable, and create a destruct() or destroy() function inside the behaviors to remove listeners and references to the target etc, and then set it to null.

[as]
// Anywhere in your code:
var myBehavior:MouseFollowBehavior = new MouseFollowBehavior(myMc);

// Somewhere in your code where you want to remove the behavior:
myBehavior.destroy(); // a public function which removes all listeners, references etc
myBehavior = null;

Conclusion

Behaviors are a cool and give clean reusable classes and I think we should use it more. It’s probably not new for you and the inheritence-example is not very realistic, but I hope it’s refreshing to look at it again. If you want to learn more design patterns, I also recommend this site.

If you think back on how you have coded in the past, what would you change now you know this pattern? BTW I want to learn more too, so don’t hesitate to leave some nice feedback below.

Oh, maybe you haven’t heard, but something unexpected will happen if you hold CTRL and click on the facebook like-button (found that in a YouTube comment; very lame).

7 responses to “Design pattern – Behaviors”

  1. Meinte says:

    Very nice writeup of this style of coding. I use it a lot as well, kind of evolved to that way of coding, the same way as you have probably. You extend sprite, and you end up with trouble and slowly end up using composition. Very strong advantage is indeed the fact that you can add multiple behaviours to different targets. One drawback though, and perhaps the biggest to this technique, is the fact that one behaviour could undo another behaviour, depending on the order of behaviour instantiation(which is really not desirable). For instance you could have BehaviourA disable a target’s mouseChildren, and have BehaviourB enable it again. It’s a drawback to look out for, and I haven’t really figured out how to cleanly work around that, but I guess every design principle has it’s pro’s and cons in the end.

  2. So you re basicly explaining the composite pattern.
    For these purposes its actually better to use a factory pattern instead of composite.

  3. maar doe ze de groeten van me! 😉

  4. Mark Knol says:

    @Meinte; Thanks for the addition! That is indeed a thing we should be aware of.

    @rackdoll; Nice, I did not know the actual name of it. It is hard to find a name of a pattern if you only know how to use it. Why do you think a factory pattern would be better?

  5. Nice article Mark!

    Did you know that if you extend Temple’s AbstractBehavior (http://code.google.com/p/templelibrary/source/browse/trunk/lib/temple/behaviors/AbstractBehavior.as) (which all the Behaviors in the Temple do) you don’t even have to store the behavior is a variable for destruction. All Behaviors of the Temple will automatically be destructed when it’s target is destructed.

    Greets, Thijs

  6. Mark Knol says:

    @Thijs Yes, the temple-lib destructs objects very nicely 😉 For the examples in my post I think it was nice to notice.

Say something interesting

Please link to code from an external resource, like gist.github.com.