Structural Patterns - Advanced ActionScript 3: Design Patterns, Second Edition (2015)

Advanced ActionScript 3: Design Patterns, Second Edition (2015)

Chapter 8. Structural Patterns

You have inadvertently witnessed the use of structure among your objects via interface inheritance and object composition, which allowed objects to possess the ability to obtain new functionality from other objects. In essence, these enabled an object’s power. Continuing with this thought, the weaker the structure, or the flimsier the support, the less empowered the objects.

Consider how reusable a Concrete class is without any inherited interface. The structure that utilizes this object is forced to bind itself to a single implementation and not to that of an interface. Of course, this would be unwise if change is a possibility. To remedy this scenario, you make use of a simple interface or abstraction that the Concrete class may subclass. Doing so properly enables polymorphism, abstracts your references, and offers flexibility among variations of implementations. You achieved greater benefits from the structuring among two classes rather than the one.

Structural patterns are concerned with the makeup of parts among both classes and objects that help to strengthen their associations with additional objects. This is what makes them really good at devising new functionality from already existing objects, sometimes forming compound patterns.

This chapter will cover the Decorator, the Adapter, the Composite, and the Facade.

The Decorator Pattern

Technical Overview

Intent: To embellish objects dynamically either visually or behaviorally, as shown in Figure 8-1.

image

Figure 8-1. Class diagram

Parts

· Abstract Component

· Concrete Component

· Abstract Decorator

· Concrete Decorator

· Client

Benefits

· Expand on an individual object without the use of inheritance.

· Additional behaviors can be added and removed to meet the needs of an application.

· Deconstruct complex objects that attempt to foresee all cases into individual wrappers.

Drawbacks

· Many pieces appear similar, which may cause confusion.

· Decorators may require specific orders.

A Comprehensive Look

For quick illustration, the Decorator pattern is the yin among objects if the Strategy pattern is the yang, as the two have very similar goals. The Decorator pattern devises strategies that embellish the visual aspects and/or the functional aspects among objects, but in an alternate solution to those of the Strategy pattern. While strategies are utilized within an object, the strategies utilized with the Decorator pattern surround the objects they alter. This is why these strategies are known as decorators.

What enables a decorator to remain apart from most delegating objects is its ability to wrap a given type object and still allow the client to view the decorated object for what it was originally. This type of wrapper is said to be transparent, as it does not attempt to mask the content from the client. It’s this transparent ability that allows any number of decorators to decorate an object without impacting the bindings between the decorated object and the client.

The object that is decorated is referred to as a component. The component that is decorated can be any object within an application. In fact, even decorators can be components of other decorators. What makes this possible is polymorphism among an interface.

A decorating object and the component that it will decorate must share an interface. This is what enables the transparency of the decorator. If many decorators will be utilized to wrap a similar interface, an abstract decorator class should be used that subclasses can extend. Because the decorator conforms to the component, the component will remain unaware of the presence of the decorator, which can preserve the integrity of your objects while still possessing the ability to lace it with new functionality.

Vignette

I’m sure there was a point in time when you purchased a poster that you were excited to hang on your wall. Perhaps it represented you at the time, or it merely tied the room together. No matter the reason, you had to have this poster. So eager to adhere it to the wall you may used push pins or even double-sided tape. These two methods would ultimately ruin your poster. The gravity on the poster would eventually cause either forms of adhesive to give, thereby tearing the poster. I was always left with the corners torn around the pushpins, forcing me to add additional holes to the poster.

Being human we learn from our mistakes; now I use frames to mount my posters to the wall. The frames allow the poster to remain intact and unchanged. Whether the frame is made of plastic, wood, or even tin, the border that surrounds the poster also adds a subtle touch.

The AS3 Cast

· Abstract Component – The interface

· The abstract component declares the interface as well as any default behaviors and/or abstract methods that its subclasses will inherit.

· Concrete Component – The Concrete Behavior

· The concrete component implements the behavior as defined by its superclass.

· Abstract Decorator – The Decorating Interface

· The abstract decorator must compliment the abstract components interface in order to defer requests to its contained component. A reference to the abstract component is contained within.

· Concrete Decorator – Transparent Wrapper

· The concrete decorator implements the behavior as defined by its superclass. It has the added benefit of intercepting the requests that are forwarded to its contained component. The concrete decorator can perform additional behaviors before or after deferring requests to that of its component.

· Client – The Messenger of the Component

· The client is any aspect of the application that messengers the component. The client knows of the abstract component and on occasion will know of the decorator.

When It’s Useful

· When the addition of behavioral or decorative ornaments are required at runtime.

· For creating graphical composites and sounds.

· For intercepting requests between a client and component.

· When reducing unused complexities of a class to reduce its size.

Demonstration

Sound is a great object for a decorator to enhance, as there are many possible decorative uses that may vary from application to application. To begin, you will need to define your abstractions for both decorators and your sound.

You know that decorators must possess the same interface as your components, but I really don’t want to have decorators subclass sound. Instead let’s devise an interface for a sound object (see Listing 8-1).

Listing 8-1. Interface ISound Exposes the Interface within flash.media.Sound

package
{
public interface ISound extends IEventDispatcher
{
function get bytesLoaded() : uint;

function get bytesTotal() : int;

function close() : void;

function extract( target : ByteArray , length : Number ,image
startPosition : Number = -1 ) : Number;

function get id3() : ID3Info;

function get isBuffering() : Boolean;

function get length() : Number;

function load( stream : URLRequest , context : SoundLoaderContext = null ) : void;

function play( startTime : Number = 0 , loops : int = 0 , sndTransform :image
SoundTransform = null ) : SoundChannel;

function get url() : String;
}
}

Once you have your interface extracted, you can then implement that interface into your AbstractSoundDecorator. This will allow the decorators to possess a similar interface to the Sound component, as shown in Listing 8-2.

Listing 8-2. AbstractSoundDecorator Implements ISound and Has the Ability to Wrap ISound Objects

package
{
public class AbstractSoundDecorator implements ISound
{
protected static var _channel : SoundChannel;
protected var _snd : ISound;

public function AbstractSoundDecorator( snd : ISound ) : void
{
_snd = snd;
}

public function get bytesLoaded() : uint
{
return 0;
}

public function get bytesTotal() : int
{
return 0;
}

public function close() : void
{
}

public function extract( target : ByteArray , length : Number ,image
startPosition : Number = -1 ) : Number
{
return 0;
}

public function get id3() : ID3Info
{
return null;
}

public function get isBuffering() : Boolean
{
return false;
}

public function get length() : Number
{
return 0;
}

public function load( stream : URLRequest , context : SoundLoaderContext = null ) : void
{
}

final public function play( startTime : Number = 0 , loops : int = 0 ,image
sndTransform : SoundTransform = null ) : SoundChannel
{
_channel = doPlay( startTime , loops , sndTransform );
return _channel;
}

public function get url() : String
{
return "";
}

public function addEventListener( type : String , listener : Function ,image
useCapture : Boolean = false , priority : int = 0 ,image
useWeakReference : Boolean = false ) : void
{
}

public function dispatchEvent( event : Event ) : Boolean
{
return false;
}

public function hasEventListener( type : String ) : Boolean
{
return false;
}

public function removeEventListener( type : String , listener : Function ,image
useCapture : Boolean = false ) : void
{
}

public function willTrigger( type : String ) : Boolean
{
return false;
}

protected function doPlay( startTime : Number = 0 , loops : int = 0 ,image
sndTransform : SoundTransform = null ) : SoundChannel
{
throw new IllegalOperationError( 'doPlay must be overridden' );
return null;
}
}
}

As you can see in Listing 8-2, your wrappers will expect a component of ISound. As you know from Chapter 3, flash.media.Sound does not contain any reference to ISound within its trait object. Therefore, to properly allow this, you need to devise an abstraction for your component, which can extend flash.media.Sound but must implement ISound to achieve this hierarchy, as shown in Listing 8-3.

Listing 8-3. Audible Extends Sound and Implements ISound

package
{
public class Audible extends Sound implements ISound
{
public function Audible( stream : URLRequest = null , context : SoundLoaderContext = null )
{
super( stream , context );
}
}
}

There are many objects that could be used as decorators, including the following:

a. A decorator that retains the state of the audio.

b. A decorator that fades the audio on play.

c. A decorator that fades the audio on stop.

d. A decorator that can pause and resume a sound.

e. A decorator that can allow infinite loops.

f. A decorator that can display the MP3 ID3 tag information.

g. A decorator that enables multiple channels for overlapping audible sound.

This demonstration will focus on the pause and resume with the addition of infinite looping. But why build a wrapper that can offer looping when it’s part of the Play method parameters? The answer is simple if you have tried to implement a pause and resume functionality along with the built-in loop parameters. Sound objects work with bytes, and when you specify a start time, the sound object plays a trimmed version of the sound. This has to do with the headers and modifying the bytes to reflect a specific position to play. The looping then continues to loop the bytes, which it currently possesses, meaning it begins from where it trimmed the bytes; see Listing 8-4 through 8-6.

Listing 8-4. InfiniteLoopDecorator

package
{
public class InfiniteLoopDecorator extends AbstractSoundDecorator
{
public function InfiniteLoopDecorator( snd : ISound )
{
super( snd );
}

override protected function doPlay( startTime : Number = 0 , loops : int = 0 ,image
sndTransform : SoundTransform = null ) : SoundChannel
{
removeEvents();
_channel = _snd.play( startTime , loops );
_channel.addEventListener( Event.SOUND_COMPLETE , repeat );
return _channel;
}

private function repeat( event : Event ) : void
{
_channel = play( 0 , 0 , null );
}

private function removeEvents() : void
{
if (_channel)
_channel.removeEventListener( Event.SOUND_COMPLETE , repeat );
}
}
}

Listing 8-5. PauseableAudibleDecorator Extends AbstractSoundDecorator

package
{
public class PauseableAudibleDecorator extends AbstractSoundDecorator implements ISoundChannel
{
protected var _position : Number = 0;

public function PauseableAudibleDecorator( snd : ISound )
{
super( snd );
}

override protected function doPlay( startTime : Number = 0 , loops : int = 0 ,image
sndTransform : SoundTransform = null ) : SoundChannel
{
stop();
removeEvents();
_channel = _snd.play( _position , loops , sndTransform );
_channel.addEventListener( Event.SOUND_COMPLETE , resetPosition );
return _channel;
}

private function removeEvents() : void
{
if ( _channel )
_channel.removeEventListener( Event.SOUND_COMPLETE , resetPosition );
}

private function resetPosition( event : Event ) : void
{
_position = 0;
}

public function get leftPeak() : Number
{
return 0;
}

public function get position() : Number
{
return 0;
}

public function get rightPeak() : Number
{
return 0;
}

public function get soundTransform() : SoundTransform
{
return null;
}

public function set soundTransform( sndTransform : SoundTransform ) : void
{
}

public function stop() : void
{
if (_channel)
{
_position = _channel.position;
trace( _position );
_channel.stop();
}
}
}
}

Listing 8-6. Client Making Use of the Decorators

package
{
public class Decorator extends Sprite
{
private var _isPlaying : Boolean = false;
protected var sound : ISound;

public function Decorator()
{
sound = new Audible( new URLRequest( "music.mp3" ) );
sound = new InfiniteLoopDecorator( sound );
sound = new PauseableAudibleDecorator( sound );
sound.play();
stage.addEventListener( MouseEvent.MOUSE_DOWN , onDown );
}

private function onDown( event : MouseEvent ) : void
{
var soundChannel : ISoundChannel = ISoundChannel( sound );
_isPlaying = !_isPlaying;
if ( _isPlaying )
{
soundChannel .stop();
}
else
{
sound.play();
}
}
}
}

What makes the decorator a great tool is how it allows you to spare the added behavior from being implemented directly into your Audio class. This way, if it’s to be used in a future application, which does not make use of pause and resume, or even the infinite loop, it does not need to bare the excess weight of such features.

FAQ

· Why might decorators be order-specific?

· Decorators provide an alternative to fixed inheritance but must adhere to the rules of inheritance, as the decorator must mimic the interface of the component it is to wrap.

· When a decorator adds a behavior, it may be to perform a specific operation before passing it on to the component. This is no different than a subclass overriding a method and calling the operation of the superclass.

· The more specific a decorator becomes, the greater the risk of disrupting the order in which operations were intended.

· If the rules of inheritance apply, does that mean two-three decorators should be the limit?

· Yes and no. This depends on the nature of the beast and It’s difficult to determine without knowing the scenario. More often than not, visual ornaments don’t require a limit, providing you’re achieving the effect you desire, but this may occur when modifying functional behavior. Since each decorator is one level away from the abstract component, it’s not likely to deal with the issues that many layers of inheritance can cause.

· Bear in mind, though, that wrapping many behaviors that don’t run in a proper order can easily grow cumbersome and have strange effects on your system. The more specific the intercepted behavior, the more complicated the chain may become in order to remain synchronized.

Related Patterns

· Visitor

· Adapter

· Composite

The Adapter

Technical Overview

Intent: To conform a class interface to one that is expected by a client of the system, as shown in Figure 8-2.

image

Figure 8-2. Class diagram

Parts

· Target

· Adaptee

· Adapter

· Client

Benefits

· Maintains the original interface of a class and utilizes it where another interface is expected.

· Allows the adaptee to optionally be seen by the client.

Drawbacks

· Adds more classes to an application.

· Adapters are often application-specific.

A Comprehensive Look

Over the course of time you may find yourself in a position of wanting to use a specific object that possesses a certain behavior, but the interface of that object varies from the interface currently supported by the application. Your first inclination may be to change the method names within the class to match those required by the client, but this breaks the Open/Closed Principle. This is where adapters are useful.

Adapters possess the ability to alter an interface by means of class or object scope. Most of the time you’ll be working with object scope to adapt your interfaces; this is due to a feature that AS3 lacks called multiple inheritance, which enables a subclass to inherit from multiple classes. Instead, class adapters must make use of subclassing the object to adapt and implementing the interface of the target. If the target lacks a defined interface that can be implemented in the adapter, refactoring should be performed.

The benefits of this will allow for what is known as two-way adapters as your adapter has the ability to be used by your client as a target and in other areas of your application used as the previously adapted adaptee.

The alternative to the class adapter is the object adapter, which uses parameterization and delegation among an interface of the adaptees it’s expecting. Because the object adapter doesn't inherit from the adaptee, the object does not require any alterations when dealing with descendants of an adaptee class, whereas class adapters do.

Object adapters specify the interface of the adaptee to which they will be supplied. This occurs most often at runtime. In order for the adapter to defer the requests of the client to the adaptee, they must possess the interface of a target that is used by the client. Much like class adapters, object adapters can become two-way by implementing the adaptee interface.

Both the class and object adapters make the appropriate referrals among the requests by the client to that of the adaptee.

Vignette

Three years ago my wife and I visited the United Kingdom for her sister’s wedding. Having a lot of work to catch up on, I brought my laptop along. My planned attempts to get work done were thwarted by lack of battery life and the incompatibility of my U.S.-compliant plug with the U.K.-compliant wall socket.

The distinct receptacles of the sockets were nothing like my two-pronged cord and reminded me of a child attempting to push a star-shaped block into the circular hole of his Fisher-Price toy. Luckily this is a well known issue, so I was able to purchase the appropriate fitting that allowed me to plug my computer into a power source. One side of the device mimicked the receptacle one would find in America, whereas the other side was noticeably the male that would fit snugly within the U.K. wall socket. The purchase was obviously a worthwhile solution at the time, but, as I have yet to travel outside the country since then, I have not had the reason to use it again.

The AS3 Cast

· Target Interface – The Intended Interface

· The target interface is known by the client and must be the exposed interface of your abstract adapter.

· Abstract Adapter – The Narrow Interface

· The abstract adapter defines the smallest possible Interface that is required for the adaptee to be utilized by the client. This makes it easier for a developer to extend it.

· Concrete Adapter – Subclass of the Abstract Adapter

· The concrete adapter is optional if you make use of the un-typed property. It may also possess a reference of its own for which the abstract operations are overridden and specified.

· Adaptee – An object of the application

· The adaptee is can be any object within your application that must overcome constraints of its interface to be utilized by a client, bound to a varied type.

· Client – The Messenger

· The client is any aspect of the application that messengers the adapter and/or the adaptee. The client knows of the target interface, and may also be familiar with the interface of the adaptee.

When It’s Useful

· Frameworks can make great use of the adapters.

· When using connecting methods of varied names.

Demonstration

Many applications, both for Web and desktop, have differing distinctions between the uses of Pause/Resume and Play/Stop. While they may have similar implementations, their indication of their roles might have distinct meanings.

Consider these terms in the scenario of a game. What nature would you expect from a method labeled Pause? Certainly you would expect the game to stop while retaining its current state.

Suppose for this demonstration you have a game and are monitoring all objects in use that must be paused, including tweening animations, movie clips, and sounds. To achieve this feat, you must devise a system that monitors all objects in your application that have reason to pause. Clearly you don’t require data to pause; therefore you assign an IPause interface to objects that require it (see Listings 8-7 through 8-9).

Listing 8-7. IPause Defines the Interface Among Objects that Possess the Ability to Advance Their State

public interface IPause
{
function pause() : void;
function resume() : void;
}

Listing 8-8. Abstract Pause Conductor Defines the Interface That Is Collected and Operated On

package
{
public class APauseConductor
{
private var _collection : IIterate;
private var _iterator : IIterator;

public function APauseConductor()
{
_collection = doCreateCollection();
}

final public function addElement( element : IPause ) : void
{
doAddElement( element );
}

final public function removeElement( element : IPause ) : void
{
doRemoveElement( element );
}

final public function pause() : void
{
doPause();
}

final public function resume() : void
{
doResume();
}

protected function doRemoveElement( element : IPause ) : void
{
_collection.remove( element );
}

protected function doAddElement( element : IPause ) : void
{
_collection.append( element );
}

protected function doCreateCollection() : IIterate
{
throw new IllegalOperationError( 'doCreateCollection must be overridden' );
return null;
}

protected function doPause() : void
{
_iterator = _collection.createIterator();
while ( _iterator.hasNext() )
{
var pauseable : IPause = _iterator.currentElement();
_iterator.next();
pauseable.pause();
}
}

protected function doResume() : void
{
_iterator.reset();
while ( _iterator.hasNext() )
{
var resumeable : IPause = _iterator.currentElement();
_iterator.next();
resumeable.resume();
}
}
}
}

Listing 8-9. Concrete Conductor, Which Implements the Specifics of the Manufactured Object

package
{
public class IPauseConductor extends APauseConductor
{
public function IPauseConductor()
{
super();
}

override protected function doCreateCollection() : IIterate
{
return new DictionaryCollection();
}
}
}

Let’s revisit the decorated audio file, since you built it for reuse. It appears that you have the perfect solution as PauseableAudibleDecorator will properly pause your sounds. The only issue here is that PauseableAudibleDecorator doesn’t possess the proper IPauseinterface. Obeying the Open/Closed Principle, your solution is adaptation.

You are confronted with two means by which you can adapt your PauseableAudibleDecorator to possess the IPause interface class and object adaption. When utilizing class adaptation, you want to inherit the abilities or your adaptee and expose the interface of the target your client will message.

Listing 8-10. AudibleIPauseAdapter Inherits the PauseableAudibleDecorator’s Interface and Exposes an Additional Interface of IPause

package
{
public class AudibleIPauseAdapter extends PauseableAudibleDecoratorimage
implements IPause
{
public function AudibleIPauseAdapter( snd : ISound )
{
super( snd );
}

public function pause() : void
{
this.stop();
}

public function resume() : void
{
this.play();
}
}
}

AudibleIPauseAdapter, as seen in Listing 8-10, inherits from AudibleIPauseAdapter and Implements the interface IPause, which in turn is utilized to defer requests from target to adaptee.

Listing 8-11. DecoratorClass Making Use of the AudbileIPauseAdapter and Passing it into IPauseConductor

package
{
public class DecoratorClass extends Sprite
{
private var _isPlaying : Boolean = false;
protected var sound : ISound;
protected var pauseconductor : APauseConductor;

public function DecoratorClass()
{
sound = new Audible( new URLRequest( "music.mp3" ) ) ;
sound = new InfiniteLoopDecorator( sound );
sound = new AudibleIPauseAdapter( sound );
sound.play();

pauseconductor = new IPauseConductor();
pauseconductor.addElement( IPause( sound ) );
stage.addEventListener( MouseEvent.MOUSE_DOWN , onDown );
}

private function onDown( event : MouseEvent ) : void
{
_isPlaying = !_isPlaying;
if (_isPlaying)
{
pauseconductor.pause();
}
else
{
pauseconductor.resume();
}
}
}
}

Because you inherited your decorator, you are not impeded from wrapping your audible with more decorators, as your adapter is a two-way adapter (see Listing 8-11). Additionally, you managed to do very little work to make your adapter, due to the inherited nature, although your adapter is limited to its superclass. This means any other subclasses or decorators will require an adapter as well.

Object adapters, on the other hand, make use of delegation and object parameterization. This allows a more flexible adapter to be present in an application.

Listing 8-12. IPausePauseableAudioDecorators Accepts Parameters of any PauseableAudibleDecorator Instance, Including Subclasses

package
{
public class IPauseAudibles implements IPause
{
protected var _pauseableAudio : PauseableAudibleDecorator;

public function IPauseAudibles( audible : PauseableAudibleDecorator ) : void
{
_pauseableAudio = audible;
}

public function pause() : void
{
_pauseableAudio.stop();
}

public function resume() : void
{
_pauseableAudio.play();
}
}
}

Listing 8-12 is not a two-way adapter currently but can be by implementing the ISound interface and delegating all requests to the _pauseableAudio reference. This, of course, requires more code than that of its counterpart.

Having demonstrated what an adapter does, you can see many of the flaws of their general usage. They adapt specific objects, while many interfaces will possess stop and play functionality, such as a MovieClip. The code you already devised to conform stop and play within an adapter is specific to objects of the given hierarchy—PauseableAudibleDecorator.

This doesn’t mean that adapters are a waste; it just means that you need to devise better ways to make them adaptable. Even though MovieClip does possess a stop and play method, the compiler won’t allow its assignment among your typed reference because it’s incompatible, though dynamic binding would allow for MovieClip to properly use the stop and play methods.

ActionScript 3.0 does allow for you to bypass compile time checking to use such dynamic binding properties of the language. It’s known as the un-typed property or wildcard annotation and is represented by an asterisk: *. Any assignment to an un-typed property will never warrant compile time errors as the reference is recognized to hold any value. They will, on the other hand, be type checked at runtime. This is not something to be overused as it’s a tightrope walk between dynamically and statically typed checking.

Listing 8-13. StopPlayToPauseResume Utilizes the Wildcard Annotation to Circumvent Providing a Fixed Type, which Limits its Reuse

public function StopPlayToPauseResume
{
protected var _stopStart:*;
public function StopPlayToPauseResume(startStop:*)
{
_stopStart = startStop;
}

public function pause() : void
{
_stopStart.stop();
}

public function resume() : void
{
_stopStart.play();
}
}

//...client
var StopPlayToPauseResume : IPause = new StopPlayToPauseResume( mc );
pauseconductor.addElement( MinimalPauseAdaption );

Listing 8-13 demonstrates the ability of reuse among varied types to make use of dynamic binding. Unfortunately, the wildcard puts the burden on the developers to ensure they maintain an awareness of what objects are being passed in.

First, you change the name of StopPlayToPauseResume to reflect a base class to which Developers can subclass. Let’s use AMinimalStopPlayToPauseResume; see Listings 8-14 through 8-17.

Listing 8-14. Reflects the New Name of StopPlayToPauseResume

package
{
public class AMinimalStopPlayToPauseResume implements IPause
{
// ....cont
}
}

Listing 8-15. Subclass MChasStartStop Extends AMinimalStopPlayToPauseResume, which will Ensure the Integrity of all MovieClips at Author-time

package
{
public class MChasStartStop extends AMinimalStopPlayToPauseResume
{
public function MChasStartStop( mc : MovieClip )
{
super( mc );
}
}
}

Listing 8-16. Subclass AudioDecoratorIPauseAdapter Extends AMinimalStopPlayToPauseResume, which will Ensure the Integrity of all PauseableAudibleDecorator at Author-time

package
{
public class AudioDecoratorIPauseAdapter extends AMinimalStopPlayToPauseResume
{
public function AudioDecoratorIPauseAdapter( startStop : PauseableAudibleDecorator )
{
super( startStop );
}
}
}

Listing 8-17. DocumentClass Making Use of Your New Narrow Adaption

package
{
public class DocumentClass extends Sprite
{
private var _isPlaying : Boolean = false;
protected var sound : ISound;
protected var pauseconductor : APauseConductor;

public function DocumentClass()
{
sound = new Audible( new URLRequest( "music.mp3" ) );
sound = new InfiniteLoopDecorator( sound );
sound = new PauseableAudibleDecorator( sound );
sound.play();

var mc : MovieClip = new SimpleMCAnimation();
addChild( mc );

var mcMinimalPauseAdaption : IPause = new MChasStartStop( mc );
var soundMinimalPauseAdaption : IPause = newimage
AudioDecoratorIPauseAdapter( PauseableAudibleDecorator( sound ) );

pauseconductor = new IPauseConductor();
pauseconductor.addElement( soundMinimalPauseAdaption );
pauseconductor.addElement( mcMinimalPauseAdaption );
stage.addEventListener( MouseEvent.MOUSE_DOWN , onDown );
}

private function onDown( event : MouseEvent ) : void
{
_isPlaying = !_isPlaying;
if (_isPlaying)
{
pauseconductor.pause();
}
else
{
pauseconductor.resume();
}
}
}
}

FAQ

· You mention that adapters are often application specific. Are they not reusable?

· Adapters are never devised in the object-oriented design phase and the reason why is simple: adapters were unnecessary if all went as planned in the designing stage.

· Remember that change is constant and you have to roll with it. The adapter allows you to roll with it in an elegant way that doesn’t break your system or require a major overhaul among client or object.

· The answer is they are not meant to be reusable as they are devised specifically for the problem at hand; this is known as the problem domain. Unless another application suffers the exact fate as this one, you most likely won’t be reusing them.

· But hey, that’s a great thing; don’t feel bad.

Related Patterns

· Proxy

· Bridge

· Decorator

The Composite

Technical Overview

Intent: To devise a data structure representing a nesting relationship among objects of a part-whole hierarchy, as shown in Figure 8-3.

image

Figure 8-3. Class diagram

Parts

· Component

· Leaf

· Composite

· Client

Benefits

· Uniformity among client interactions and the data.

· New components are easy to create and integrate.

Drawbacks

· Limited type enforcements: any object that inherits from components can be added safely even if they are not meant to work with your system.

· It’s difficult to follow conceptually.

A Comprehensive Look

The Composite pattern is a very convenient pattern to use with ActionScript 3.0 because the foundation of the display list was built on the idea of the composite. The composite represents a tree structure where each object is aware of any objects nested within. The structure should be as efficient as possible for traversing, adding and removing elements, ordering, etc.

For efficiency, the client should not be able to distinguish one element from another, meaning compositions of objects from individual objects. This is important because the composite will forward one request to each and every component contained within the structure without prejudice.

ActionScript 3.0’s display list refers to these as DisplayObjectContainer and a DisplayObject, whereas the Composite pattern refers to them as composite and leaf. In XML, such components are referred to as nodes.

A composite is an element that contains any number of additional composites or leaves. Therefore, it must define operations that access and manipulate its nested elements.

A leaf, on the other hand, has no children. This is the main distinction between the two. In order for the two to appear as indistinguishable from the client as possible, they must both utilize inheritance to which they both gain a common interface. This enables the client to interact with either uniformly.

This interface is how the clients will see these two components. The component’s interface should account for as much commonality between the leaf and the composite as possible. This further allows the client to remain unaware of the object’s true identity. You will be confronted with the decision to compromise either safety or transparency for optimal conditions when considering who should define the operations of child management.

Optionally, components may possess references to their parents. This may assist in the traversing upwards among the structure and supports the addition of the chain of responsibility, which can facilitate the removal of an element. Otherwise, a composite traverses downwards and outwards towards the leaf/leaves.

Vignette

There are several divisions of the U.S. military: Army, Marines, Air Force, and Navy. Each division contains smaller divisions until a specific unit size has been reached. I can’t begin to explain the hierarchy of each branch. What I do know is the President resides at the top and at the bottom the newest recruits. Everything in between is looking to pass their demands off to those of lesser rank. This is known as the chain of command.

The president will never physically tell a new recruit what to do but will pass the message to his immediate successor until the message has been received by the individuals of a particular division within a military branch.

While specific branches can be targeted, all branches can be issued the same requests in a time of war. From the top of the Armed Forces to the lowest on the poles, all will receive the same commands. This will trigger their specific operations depending on their role within the military.

Such structuring within the Armed Forces allows one message to be carried out similarly and indiscriminately among high ranking officers and the grunts. This also speeds up the dissemination of information by simultaneously passing the operation down all channels.

The AS3 Cast

· Abstract Component – The interface

· The abstract component defines the operations common to both the leaf and composite; it also defines the interface that the client will utilize to interact with the structure.

· Leaf – An object

· The leaf may represent any object within your application providing it possesses no children that must be traversed.

· Composite – An object container

· The composite maintains references to its nested elements in addition to the implementations defined by the component to manage said children.

· Client – The Messenger of the Component

· The client is any aspect of the application that messages the interface of the component. The client remains unaware of which element, either leaf or composite, it’s manipulating.

When It’s Useful

Many objects require the same message to perform similar behaviors at the same time, such as objects that manage or oversee pausing, resuming, destroying, etc.

Demonstration

In previous demonstrations I’ve portrayed the means by which you can pause and resume any object that possesses the IPause interface. That was an example of a “loose” type composite. You made use of an individual composition and leaf nodes, which were objects that implemented IPause. I refer to this as “loose” because your two objects did not derive from a common component other than the built-in object.

Suppose you have a MovieClip that contains a given number of nested MovieClips as well as Sprites. If you wanted to pause your MovieClip instances, you could define an operation within the main MovieClip that targets only MovieClips and ensures they stop or play depending on the situation. Such a method is shown in Listing 8-18.

Listing 8-18. Utilizes Recursion Among any Descendants Within a Found MovieClip in Order to Pause or Resume Any Instances of MovieClips

public function traverse( mc : MovieClip , bol : Boolean ) : void
{
if ( bol )
{
mc.play();
}
else
{
mc.stop();
}

if ( mc.numChildren > 0 )
{
for ( var i : int = 0 ; i < mc.numChildren ; i++ )
{
var innerds : DisplayObject = mc.getChildAt( i );
if ( innerds is MovieClip )
{
traverse( MovieClip( innerds ) , bol );
}
}
}
}

Listing 8-18 demonstrates how a MovieClip composite operates on the leaf nodes and continues to traverse all possible nested composites. As mentioned earlier, because AS3’s display list is built around the Composite, you are able to tap into its ability with a bit of ingenuity.

Dealing with DisplayObjects is very convenient with the Composite pattern, as DisplayObject possesses many of the necessary commonalities among the more elaborate DisplayObjects such as Sprite, MovieClip, and Bitmap. You begin this menu system by making your component for all leaf and composite objects to extend, as shown in Listing 8-19.

Listing 8-19. Component Class

package
{
public class Component extends Sprite
{
private static var _counter : int = 0;
protected var _parentComposite : Component;
protected var _identity : int;
protected var _arCollection : ArrayCollection;

public function Component()
{
_identity = ++_counter;
}

final public function addComponent( cmpt : Component ) : void
{
doVerifyCollection();
doAddComponent( cmpt );
}

final public function removeComponent( cmpt : Component ) : void
{
if ( _arCollection )
doRemoveComponent();
}

final public function operate() : void
{
doOperate();
}

final public function get parentComposite() : Component
{
return _parentComposite;
}

final public function set parentComposite( parentComposite : Component ) : void
{
_parentComposite = parentComposite;
}

public function get identity() : int
{
return _identity;
}

protected function doOperate() : void
{
throw new IllegalOperationError( 'doOperate must be overridden' );
}

protected function doAddComponent( cmpt : Component ) : void
{
throw new IllegalOperationError( 'doAddComponent must be overridden' );
}

protected function doRemoveComponent() : void
{
throw new IllegalOperationError( 'doRemoveComponent must be overridden' );
}

private function doVerifyCollection() : void
{
if ( !_arCollection )
_arCollection = new ArrayCollection();
}
}
}

The component class may look overwhelming, but it’s rather simple. The component class takes advantage of being the abstract class and declares common attributes and behaviors that will be utilized by both leaf and composites. The static variable, _counter, is intended to keep track of the number of components utilized to aid in the removal of the appropriate object. Its assigned value is stored in the _identity attribute.

To make use of code you have already created, you might recognize our _arCollection from the Iterator pattern. Because the Composite pattern makes use of structured data, you may optionally rely on a means to iterate such data, and the Iterator does this very well, as you will see. Lastly, _parentComposite will maintain the parent composite for every component utilized. This will help you to notify all composites upwards, which can then notify their components of updates, just like the chain of command (see Listing 8-20).

Listing 8-20. Leaf Component

package
{
public class Leaf extends Component
{
public function Leaf()
{
super();
}

override protected function doOperate() : void
{
// your operation goes here
}
}
}

The leaf component overrides the doOperate method that enables the leaf to handle the operation as appropriately required.

Listing 8-21. Composite Component

package
{
public class Composite extends Component
{
public function Composite()
{
super();
}

override protected function doAddComponent( cmpt : Component ) : void
{
cmpt.parentComposite = this;
_arCollection.append( cmpt );
addChild( cmpt );
}

override protected function doOperate() : void
{
var it : IIterator = _arCollection.createIterator();
while ( it.hasNext() )
{
var cnent : Component = it.currentElement() as Component;
it.next();
cnent.operate();
}
}
}
}

In Listing 8-20, the composite component overrides the doAddComponent and ensures the nested child has the appropriate pointer to its parent composite. The nested component is then added to the display of the composite as well as composite’s collection of contained children.

In efforts to conserve memory, composites do not instantiate a collection unless an object is being added. The method addComponent within a component makes use of a template method to ensure a collection exists before allowing a component to be added. This allows a composite the opportunity to initialize a collection, if one does not exist. doAddComponent is the hook that composite taps into.

The operate method does not have to be strictly operate and should reflect the proper behavior for which it’s used. In the case of disposing of objects, you could call the operate method within the dispose component. The dispose method then just needs to be properly overwritten.

Optionally, any number of methods can be implemented to make use of the Composite pattern. The Composite pattern makes controlling a large number of objects greatly simplified, as one message is forwarded to each and every component of any given composite. Along with dispose, you could add enable, disable, hide, reveal, etc.

In Listing 8-8, APauseConductor from the Adapter pattern made use of a collection among objects, which implemented the IPauseable object. The conductor is very reminiscent of the composite element, in that it sends out a message to every collection that it contains. In that particular example, in the adapter, it was to make use of pausing sound and movie clips. The example shows that both sound and movie clips were treated equally; however, that may not always be the desire of the developer. You will make use of the IPause interface, along with the composite pattern, to demonstrate how you can separate pauseable movies and pauseable sounds, as well as trigger each individually or simultaneously. The structure you will devise will reflect Figure 8-4.

image

Figure 8-4. The data structure of pauseable objects

You begin by establishing the abstract component, which will contain all necessary default behaviors and attributes that will be inherited by any Component subclass; see Listing 8-22.

Listing 8-22. Component

package
{
public class Component
{
private static var _counter : int = 0;
protected var _parentComposite : Component;
protected var _identity : int;

public function Component()
{
_counter++;
}

public function get identity() : int
{
return _identity;
}

final public function get parentComposite() : Component
{
return _parentComposite;
}

final public function set parentComposite( parentComposite : Component ) : void
{
_parentComposite = parentComposite;
}
}
}

Next, you need to define the component that is specific to this project and possesses the specific behaviors required by your application. You’ll call this PauseableComponent, as shown in Listing 8-23.

Listing 8-23. PauseableComponent

package
{
public class PauseableComponent extends Component
{
public function PauseableComponent()
{
super();
}

final public function pause() : void
{
doPause();
}

final public function resume() : void
{
doResume();
}

protected function doResume() : void
{
throw new IllegalOperationError( 'doResume must be overridden' );
}

protected function doPause() : void
{
throw new IllegalOperationError( 'doPause must be overridden' );
}
}
}

PauseableComponent declares necessary methods particular to this application, which will also need to be overridden by any subclasses. You’ll begin with the leaf, which will be labeled PauseableLeaf. Being that this application will pause both movie clips and sound, you must find a common behavior that you can specify as the parameter of your leaf; and this will be IPause, as shown in Listing 8-24.

Listing 8-24. PauseableLeaf Accepts a Parameter of IPause Objects

package
{
public class PauseableLeaf extends PauseableComponent
{
protected var _iPause : IPause;

public function PauseableLeaf( _pauseable : IPause )
{
super();
_iPause = _pauseable;
}

override protected function doResume() : void
{
_iPause.resume();
}

override protected function doPause() : void
{
_iPause.pause();
}
}
}

Lastly, you need to devise the composite that you’ll call PauseableComposite. You may have noticed that neither Component nor PauseableComponent declared the operations for child management. This is because they will be placed here, in PauseableComposite. All that is left to do now is to assign the appropriate implementations shown in Listing 8-25.

Listing 8-25. PauseableComposite

package
{
public class PauseableComposite extends PauseableComponent
{
protected var _arCollection : ArrayCollection;

public function PauseableComposite()
{
super();
}

public function addComponent( pauseable : PauseableComponent ) : void
{
doVerifyCollection();
_arCollection.append( pauseable );
}

public function removeComponent( pauseable : PauseableComponent ) : void
{
}

override protected function doResume() : void
{
var it : IIterator = _arCollection.createIterator();
while ( it.hasNext() )
{
var cnent : PauseableComponent = it.currentElement() as PauseableComponent;
it.next();
cnent.resume();
}
}

override protected function doPause() : void
{
var it : IIterator = _arCollection.createIterator();
while ( it.hasNext() )
{
var cnent : PauseableComponent = it.currentElement() as PauseableComponent;
it.next();
cnent.pause();
}
}

private function doVerifyCollection() : void
{
if ( !_arCollection )
_arCollection = new ArrayCollection();
}
}
}

It’s important to note that the composite element will always be the first component invoked. In any Composite pattern structure, the main node of the pattern is expected to be a composite element that contains any number of components. The message of this composite will continue through any nested composites and/or leaves until no further components can forward the message. Therefore, strategically, one could retain references to particular nodes within a data structure to be targeted specifically.

Listing 8-26. The DocumentClass that Builds the Data Structure

package
{
public class DocumentClass extends Sprite
{
private var _compositeOfIPauseObjects : PauseableComponent;
private var _sndComposite : PauseableComponent;
private var _mcComposite : PauseableComponent;
private var _isPlaying : Boolean = false;

public function DocumentClass()
{
var sound : ISound = new Audible( new URLRequest( "music.mp3" ) );
sound = new InfiniteLoopDecorator( sound );
sound = new PauseableAudibleDecorator( sound );
sound.play();

var mc : MovieClip = new SimpleMCAnimation();
addChild( mc );

var mcMinimalPauseAdaption : IPause = new MChasStartStop( mc );
var soundMinimalPauseAdaption : IPause = newimage
AudioDecoratorIPauseAdapter( PauseableAudibleDecorator( sound ) );

var mcLeaf : PauseableLeaf = new PauseableLeaf( mcMinimalPauseAdaption );
var sndLeaf : PauseableLeaf = new PauseableLeaf(soundMinimalPauseAdaption);

var pauseableMCComposite : PauseableComposite = new PauseableComposite();
pauseableMCComposite.addComponent( mcLeaf );

var pauseableSndComposite : PauseableComposite = new PauseableComposite();
pauseableSndComposite.addComponent( sndLeaf );

var iPauseComposites : PauseableComposite = new PauseableComposite();
iPauseComposites.addComponent( pauseableMCComposite );
iPauseComposites.addComponent( pauseableSndComposite );

_compositeOfIPauseObjects = iPauseComposites;
_sndComposite = pauseableSndComposite;
_mcComposite = pauseableMCComposite;
stage.addEventListener( MouseEvent.MOUSE_DOWN , onDown );
}

private function onDown( event : MouseEvent ) : void
{
if ( _isPlaying )
{
_compositeOfIPauseObjects.pause();
}
else
{
_compositeOfIPauseObjects.resume();
}
_isPlaying = !_isPlaying;
}
}
}

The code in Listing 8-26 demonstrates the ability to pause all PauseableLeafs contained within the system. While there are only two leaves, this may not seem like such a large feat, but you are now able to toggle their ability to pause and resume at once. Optionally, if you were to retain references to pauseableMCComposite and pauseableSndComposite, you could possess a finer level of control among which a given IPause collection should pause. Consider how video games pause; often the music continues to play in the background yet animations pause to bring focus on the pause menu.

Additional methods can be added to further aid such necessity among child management as performed with XML, although such methods will vary on the data within the structure. It’s quite common to see the Composite pattern make use of the already built-in functionality of the composite by the display list for convenience. This does not mean that the Composite pattern does not have its place. On the contrary, it means you use it all the time, just “loosely.”

When working with data that must be maintained, managed, and simplified by the means of operating on many at once and indistinguishably, the Composite pattern is a fantastic tool.

Related Patterns

· Chain of Responsibility

· Iterator

· Decorator

· Visitor

· Flyweight

Facade

Technical Overview

Intent: To provide a unified interface to a set of interfaces in a subsystem, as shown in Figure 8-5.

image

Figure 8-5. Class diagram

Parts

· Facade

· Subsystem Objects

· Client

Benefits

· Conceals the complexities of the subsystems.

· Reveals a simple interface that is more efficient.

· Subsystems can still be utilized even if a façade is in place.

· Loosens the couplings among subsystems and the client.

· Localizes the subsystems that work together.

Drawbacks:

· Additional classes.

A Comprehensive Look

In a computer language, an object is known to possess particular attributes and behaviors, which accompany it. The behaviors and attributes that it possesses ultimately define how we refer to the object itself. When I use the words “quack” and “woof” you probably think of a duck and dog. If I say “lore” and “hock” you might not realize the connection to which they refer because you are unfamiliar with these parts. We tend to give more focus to the aspects that we interface with or can observe.

There is an appropriate name for this: facade. A facade, by its definition, refers to the aspect that is publicly seen and by the most common vantage point. More accurately, it’s the public entry. In programming terms, it’s merely the interface that the developers and the client will use.

A facade provides two unique applications. The first is to conceal the inner workings of a complicated system from the client. This reduces the amount of possible subsystems that need to be referenced and targeted in an application. Secondly, it can reduce complexities among subsystems and the knowledge required to use them in collaborations properly by exposing a simplified interface that any developer can understand.

A facade appropriately delegates the request of the client to the appropriate subsystem.

Vignette

Ones and zeros make up the computer language known as binary language. Very few are able to read and write in binary, yet nearly everyone is capable of using a computer. Even we developers who understand that binary language would be helpless if it were not for the graphical user interface (GUI) of the operating system (OS), let alone our ActionScript editors. Rather than requiring users to understand binary, memory allocation, and retrieval, the system operators have provided a way to visually and textually work with the many parts of the computer such as the memory, the processor, etc. Those familiar enough with the aspects of their OS are often able to take advantage of the built-in language to achieve the lower level experiences they need.

The AS3 Cast

· Abstract Facade – The Interface

· The abstract facade provides the interface to which all subclasses will implement. Additionally, the abstract facade defines any factory objects necessary for determining the appropriate subsystem interfaces for the application.

· Concrete Facade – Implements Specifics

· The concrete facade specifies the implementations of the interface that facilitate the forwarding of client requests to any subsystems appropriately. The concrete facade also implements the logic to manufacture the concrete subsystems.

· Concrete Subsystems – Individual Systems

· The concrete subsystems may be any object within your application that increases complications within the orchestration among other objects. The concrete subsystems will never have any knowledge of the facade.

· Client – The Messenger of the Facade

· The client may be any aspect of your application that has knowledge of the abstract facade. On occasion, the client may have limited knowledge among the interface of one or more of the subsystems within the facade.

When It’s Useful

· For devising uniformity among a custom API.

· For funneling multiple interfaces into a singular instance.

Demonstration

A video player is made up of three top-level objects: Video, NetStream, and NetConnection. All three objects work in collaboration to appropriately deliver a video to the user in a manner that reflects the choices he or she makes (such decisions may be to pause, replay, etc.); seeListing 8-27.

Listing 8-27. Demonstrating the Assembly among Video, NetStream, and NetConnection

_netConnection = new NetConnection();
_netConnection.addEventListener( NetStatusEvent.NET_STATUS , doHandleConnectStatus );
_netConnection.connect( null );
_netStream = new NetStream( _netConnection );
_netStream.addEventListener( NetStatusEvent.NET_STATUS , doHandleNetStatus );
_netStream.client = this;
_vid = new Video();
_vid.attachNetStream( _netStream );
addChild( _vid );

In order to handle such requests, the client needs to be aware of all three objects in order to devise the appropriate associations and delegate the appropriate behaviors to the proper object. NetStream will handle the majority of the forwarded behavior, yet Video and NetConnectionare required to close a connection. Let’s add a fourth object to the mix, GUI.

GUI displays the current state of the player and allows the user to control their watching experience. As a fourth component that the client may need to be aware of, your system can become overly complicated and cumbersome for another developer to manage.

The facade reduces the intricate knowledge of required subsystems by revealing a singular interface of high-level operations of which a developer and client must be aware. To enable a loosely coupled relationship between the facade and the subsystems and even the client, an abstract facade can be layered and the implementation of an abstract factory should be utilized to instantiate the appropriate subsystems.

While a video player does not truly have an overly complicated arrangement, your client knows that four video components should not be necessary. As they are also related among one another, they should be localized for any necessary maintenance. As an interface will be required for the client to communicate with your encapsulated objects, this is a good time to devise a video player facade.

You begin with an abstraction of your facade that you will modify as you layer your interface to meet the requirements of your system; see Listings 8-28 and 8-29.

Listing 8-28. AbstractVideoPlayerFacade is the Abstraction of Your Facade and Provides Default Functionality Only in This Example

package
{
public class AbstractVideoPlayerFacade extends Sprite
{
protected var _vid : Video;
protected var _ns : NetStream;
protected var _nc : NetConnection;

public function AbstractVideoPlayerFacade()
{
_nc = doGetNetConnection();
_nc.connect( null );
_ns = doGetNetStream();
_vid = doGetVideo();
_vid.attachNetStream( _ns );
addChild( _vid );
}

public function playURL( url : String ) : void
{
_ns.play( url );
}

final public function close() : void
{
_nc.close();
_vid.clear();
}

protected function doGetVideo() : Video
{
throw new IllegalOperationError( 'doGetVideo must be overridden' );
}

protected function doGetNetStream() : NetStream
{
throw new IllegalOperationError( 'doGetNetStream must be overridden' );
}

protected function doGetNetConnection() : NetConnection
{
throw new IllegalOperationError( 'doGetNetConnection must be overridden' );
}
}
}

Listing 8-29. PauseResumeVideoPlayerFacade Extends AbstractVideoPlayerFacade and Implements the Appropriate Behaviors

package
{
public class PauseResumeVideoPlayerFacade extends AbstractVideoPlayerFacade
{
public function PauseResumeVideoPlayerFacade()
{
super();
}

public function pause() : void
{
_ns.pause();
}

public function resume() : void
{
_ns.resume();
}

override protected function doGetNetConnection() : NetConnection
{
return new NetConnection();
}

override protected function doGetNetStream() : NetStream
{
return new NetStream( _nc );
}

override protected function doGetVideo() : Video
{
var vid : Video = new Video();
vid.width = 640;
vid.height = 320;
return vid;
}
}
}

Thus far, you have introduced four public methods that delegate the appropriate requests to the respective objects. You can continue to further support necessary interfaces as well as allow visibility among the objects for low-level operations, which your interface does not account for. Such low-level operations remain the responsibilities of Video, NetStream, and NetConnection. These objects should be extended and implement the appropriate behavior that your application requires.

The facade Is merely the interface and the necessary operations to properly defer the clients’ requests to the appropriate objects. This enhances the cohesion of the client and localizes the objects that make up the video player; see Listing 8-30.

Listing 8-30. Client Use the Facade, Unaware of the Subsystems Involved

vid = new PauseResumeVideoPlayerFacade();
addChild( vid );
vid.playURL( "superfad_preguntas.flv" );
vid.pause();
vid.play();

FAQ

· The facade sounds like an interface to me. How is it different from an interface?

· It’s actually good that it does resemble an interface to you because that is what the facade wants you to believe. The facade acts as an interface to an object so that the client does not need to be aware of the many objects and their interfaces that, without the facade there to conceal them, it would.

· The major difference is that a facade intends to make it easier to use the many subsystems to which it coordinates, but should advanced developers require the ability to make use of the objects without the use of a facade, they can do just that.

Related Patterns

· Abstract Factory

· Mediator

Chapter Summary

Structural patterns shed light on ways to enable an extension of requirements by utilizing additive support. This is accomplished either with inheritance or composition to devise new associations.

The concept of adding allows objects to remain more cohesive, more reusable, and increases flexibility, and additionally, to use larger objects.

Key Points

· The Decorator pattern offers added embellishment among like interfaces.

· The Adapter pattern adapts an already existing object to a similar interface.

· The Composite delivers one message from the client to all composed Components.

· The Facade reduces the intricacies of object collaborations from a client.