flash event_model_weak_coupling

The power of weak coupling

The code in the previous article functions for the moment with no problems. If we need to modify certain elements of our application like the name of certain objects, a cascading modification of the code appears.

To illustrate the weaknesses in our previous code imagine the following timeline:

Mark, a new ActionScript 3 developer comes into your team and takes on the project of resizing the window that we developed together. Mark decides to rename the myWindow clip as myMainWindow which necessitates an update to the code.

Mark therefore replaces the line which allows the subscription of the Event.ENTER_FRAME event in the window clip.

Here is the new version of the clickButton

function clickButton ( pEvt:MouseEvent ):void
{
width = Math.random()*400;
height = Math.random()*400;
trace("listener function triggered");
trace("subscription to the Event.ENTER_FRAME event");
if ( !myMainWindow.hasEventListener ( Event.ENTER_FRAME ) )
{
myMainWindow.addEventListener ( Event.ENTER_FRAME, resize );
}
}

On compilation, Mark realizes that there are multiple errors displayed in the output panel. On numerous tries the following error is displayer

1120: Access to the myWindow property is not defined.

This error shows that part of our code calls the myWindow object which is no longer defined in our application. In effect we target the myWindow occurrence multiple times in the resize function but it no longer exists as Mark renamed it.

Mark therefore decides to update all of the resize function code and gets the following code:

function resize ( pEvt:Event ):void
{
trace("execution of the resize function");
myMainWindow.width -= ( myMainWindow.width – width ) * .3;
myMainWindow.height -= ( myMainWindow.height – height ) * .3;
if ( Math.abs ( myMainWindow.width – width ) < .5 && Math.abs (
myMainWindow.height – height ) < .5 )
{
myMainWindow.removeEventListener ( Event.ENTER_FRAME, resize );
trace("arrived");
}
}

On compilation everything works again!

Unfortunately for each modification of the occurrence name of the myMainWindow clip, we need to update all of the code. This is because the coupling between objects is very strong.

Eric who has just read an article on weak coupling therefore proposes using essential information in the resize function brought about by all broadcast object events.

Bear in mind that when an event is broadcast, the listener functions are notified of the event, then an event object is passed in a parameter in each listener function. In all event objects resides a target property advising the listener function of the source object, the author of the event.

By targeting this property, we make the coupling weak between our objects and the code more flexible.

Let’s modify the resize function code in order to target the target property of the event object:

function resize ( pEvt:Event ):void
{
trace("execution of the resize function");
var objectBroadcaster:DisplayObject = pEvt.target as DisplayObject;
objectBroadcaster.width -= ( objectBroadcaster.width - nWidth ) * .3;
objectBroadcaster.height -= ( objectBroadcaster.height - nHeight ) * .3;
if ( Math.abs ( objectBroadcaster.width - nWidth ) < .5 && Math.abs (
objectBroadcaster.height - nHeight ) < .5 )
{
objectBroadcaster.removeEventListener ( Event.ENTER_FRAME, resize
);
trace("arrived");
}
}

If we compile, the code works. From now on the resize function makes reference to the myMainWindow clip by the intermediary of the target property of the event object.

When the occurrence name of the myMainWindow clip is modified, the targetproperty referring to the source object of the event allows us to target the author object of the event without even knowing its name or position.

For all future modifications of the occurrence name of the myMainWindow clip, no modification will have to be brought to the resize listener function. We therefore win in flexibility and our code is easier to maintain!

A few days later mark decides to overlap the myMainWindow clip in another clip called container. Happily our inter-object coupling is weak and only one line will have to be modified:

function clickButton ( pEvt:MouseEvent ):void
{
width = Math.random()*400;
height = Math.random()*400;
trace("listener function triggered");
trace("subscription to the Event.ENTER_FRAME event");
if ( !container.myMainWindow.hasEventListener ( Event.ENTER_FRAME ) )
{
container.myMainWindow.addEventListener ( Event.ENTER_FRAME,
resize );
}
}

At the heart of our resize listener we make reference to the myMainWindow clip by the target property which contains a reference to the myMainWindow clip

Marc is happy, thanks to weak coupling his need to update the code is practically nothing.

Worth remembering

  • When an event no longer needs observing, we delete the listener with the removeEventListener method
  • The removeEventListener method is the only way of deleting the event listener
  • The name of each event is stored in the static property of the corresponding class to the event in question
  • Use the target or currentTarget property of event objects to guarantee weak coupling between objects

Flexibility of code

The interest of the new event model in ActionScript 3 doesn’t stop there. It was impossible before to carry out multiple callback functions on the same event.

The following ActionScript shows this limitation of the old event model:

function clickButton1 ( )
{
trace("click 1 on the button");
}
function clickButton2 ( )
{
trace("click 2 on the button");
}
myButton.onRelease = clickButton1;
myButton.onRelease = clickButton2;

We define two functions to manage the onRelease event on the myButton button. By using the event model it was impossible to define multiple functions to manage a same event. In ActionScript 1 and 2, only one function could be defined to generate a specific event.

In our code, only the clickButton2 function will be assigned as manager of the onRelease event, as the last assignment carried out on the onRelease event is the clickButton2 function. The second assignment replaces the first.

The idea of even the new ActionScript event model is based on the idea of one to multiple relations which is the definition of the Observer design model.

We can therefore subscribe multiple listeners to the same event. Let’s take a simple event constituting of a button and of two listener functions.

In a new Flash document create a button symbol and place an occurrence of the button on the stage and call it myButton. On an AS layer type the following code:

myButton.addEventListener ( MouseEvent.CLICK, clickButton1 );
myButton.addEventListener ( MouseEvent.CLICK, clickButton2 );
function clickButton1 ( pEvt:MouseEvent ):void
{
// display : clickButton1 : [object MovieClip] : myButton
trace( "clickButton1 : " + pEvt.target + " : " + pEvt.target.name );
}
function clickButton2 ( pEvt:MouseEvent ):void
{
// display : clickButton2 : [object MovieClip] : myButton
trace( "clickButton2 : " + pEvt.target + " : " + pEvt.target.name );
}

We subscribe two listener functions to the MouseEvent.CLICK event. When the button is clicked the two listener functions are notified about the event.

The notification order depends on the order in which the listeners were subscribed. Here therefore is our example of the clickButton2 function which will be executed after the clickButton1 function.

In the same way, a single listener function can be reused for different objects. In the following code the listener function rotation is used for the three buttons. Our first reflexes could leave us to produce the following code:

myButton1.addEventListener ( MouseEvent.MOUSE_DOWN, rotation );
myButton2.addEventListener ( MouseEvent.MOUSE_DOWN, rotation );
myButton3.addEventListener ( MouseEvent.MOUSE_DOWN, rotation );
function rotation ( pEvt:MouseEvent )
{
if ( pEvt.target == myButton1 ) myButton1.rotation += 5;
else if ( pEvt.target == myButton2 ) myButton2.rotation += 5;
else if ( pEvt.target == myButton3 ) myButton3.rotation += 5;
}

Remember that the target property allows you to dynamically reference the clicked object:

myButton1.addEventListener ( MouseEvent.MOUSE_DOWN, rotation );
myButton2.addEventListener ( MouseEvent.MOUSE_DOWN, rotation );
myButton3.addEventListener ( MouseEvent.MOUSE_DOWN, rotation );
function rotation ( pEvt:MouseEvent )
{
pEvt.target.rotation += 5;
}

The rotation function is therefore reuseable for any object which can rotate.








Adobe Training center


Mediabox Training Centre © 2000 - 2008 All rights reserved.
Adobe Authorized Training Centre. State convention under number 25 14 02167 14.
Mediabox : SARL au capital de 62.000€ - Activity number: 25 14 02167 14 - SIRET : 493 716 468 00027
MEDIABOX, 102 Avenue des Champs Elysées, 75008 PARIS - Tel. +33(0)2.31.91.96.89 - Fax. +33(0)2.72.68.56.42