• No results found

The Template Method Pattern

The Template Method pattern defines the skeleton of an algorithm in an operation, deferring steps to subclasses for implementation. Figure 7-2 shows the class diagram.

Figure 7-2. Template Method pattern class diagram

This pattern has the following parts: Abstract class • Concrete class • Client •

The pattern’s benefits are as follows: Hooks that promote overriding •

Fixed ordering among algorithmic steps •

Specific extension points •

A Comprehensive Look

The operations in an abstract class, that define the skeletal composition of the template method, are similar to the bullet points in a topic outline that emphasize a building block of the topic. These bare-boned operations outline the skeleton of an algorithm that a subclass elaborates on. Thus these points form the template, which each subclass needs to model itself to arrive at an end behavior.

Although the subclasses implement specifics, they don’t dictate the order in which the operations are used. This is left up to the abstraction in an effort to localize and maintain the logic among operations.

The Template Method pattern defines the order of the skeletal operations to create uniformity among its subclasses. Primitive operations, which are overridden, are often indicated using the prefix do. (You must prevent subclasses from overriding the template method itself.) The template method increases class cohesion, and provides the necessary hooks into which subclasses can tap.

Vignette

The phrase “Mother knows best” may not always hold true, but a mother does know how she chooses to raise her children. She protects and nurtures them, but she also provides structure. If a mother changes her rules on a daily basis, her children don’t know what path to follow; this is why it’s very important for the mother to solidify this path, and ensure that her children remain on it.

But a mother can’t be there every second of the day. On occasion, the duties of a mother may be passed to a babysitter for a few hours. The babysitter isn’t the mother, doesn’t know what’s best for the children, and has no idea what the parents plan for their children’s future. This is because the babysitter is an individual and doesn’t have the same concerns as the mother. Plus, watching the children is just a way for the babysitter to earn a few extra dollars.

To ensure that the babysitter represents the mother’s presence, the mother must enforce guidelines to be followed while caring for her children. This is accomplished via itemized rules as well as precautionary actions to be taken if anything goes wrong. These actions are detailed and given due diligence on the part of the mother, but what isn’t expressed is the means by which these actions are to be carried out.

The AS3 Cast

In AS3, the Template Method pattern’s parts are as follows:

Abstract class: Exposes the interface and defines the order of operations necessary to fulfill •

the obligations of an algorithm. The abstraction must ensure the prevention of its template method.

Concrete class: Defines the specific implementations of behaviors specified by its abstraction. •

Client: The messenger of the concrete class, which by messaging may trigger the template •

method. This may be the concrete class itself.

When It’s Useful

The Template Method pattern is useful for doing the following: Providing uniformity among subclasses

Preventing alteration of sequences •

Enforcing factory methods •

Example

You’ve seen how modularity can promote flexibility. While modules remain in a hierarchy, they can be interchanged, allowing for greater flexibility. Interfaces tie modules together by allowing clients to message their behaviors using a common signature, but an interface doesn’t achieve uniformity of an algorithm to be performed. The Template Method localizes the steps of an algorithm so you can make changes in one place rather than many, thus subclassing the abstraction.

Sections of a web site often use template methods to enforce expected behaviors when a section initiates or exits. Consider these sections of an ActionScript 3 web site: Portfolio, News, About You, Home, and Contact. Each section is written as its own module, and the modules are tied together by their common interface.

Often an abstract class known as ABaseClass defines this interface and extends the flash.display.Sprite object for its interactive properties. ABaseClass’s duties are those of any abstract class: it supplies default behaviors and declares any abstract methods expected to be overwritten by its subclasses. It also creates the inherited interface used by all subclasses that are messaged by the application framework.

Listing 7-9 shows an abstract class that defines an interface that is implemented by all subclasses. ABaseSection also implements a few defaults that are shared among the subclasses. The problem demonstrated in this abstract class is the expectation that subclasses override behaviors, which may already implement a default behavior.

Listing 7-9. ABaseSection acting as an abstract class among modular site sections

package {

public class ABaseSection extends Sprite {

protected var _paused : Boolean; protected var _width : Number; protected var _height : Number; protected var _shell : IShell; public function ABaseSection() {

} /*

* Init is the first to be triggered override and load assets */

public function init( shell : IShell ) : void {

_shell = shell;

shell.addEventListener( ShellEvent.EXIT_SECTION , outro ); }

/*

* Pause expects videos, animations, timers, sounds etc... to pause. */

public function pause() : void {

_paused = true; }

/*

* Unpause expects videos, animations, timers, sounds etc... to resume. */

public function unpause() : void {

_paused = false; }

public function intro() : void {

} /*

* destroy expects memory to be released. */

public function destroy() : void {

_shell.removeEventListener( ShellEvent.EXIT_SECTION , outro ); }

public function updateLayout( width : Number , height : Number ) : void {

_width = width; _height = height; }

protected function outro() : void {

} } }

Any subclass that does as intended and overrides these abstract methods may forget to use the default implementation of the superclass. The problem is that not only do your subclasses require their own logic for their section of the web site, but the superclass in this case, also expects them to recognize when they need to call a

superclass method. The dilemma becomes apparent when a subclass extends the abstraction, as shown in Listing 7-10.

Listing 7-10. Home is a specialized type of section and overrides the intro operation to support its own code

package {

public class Home extends AbstractSection {

public var introCopyAnimation : MovieClip; public var background : Bitmap;

public var backgroundContainer : Sprite; public function Home()

{

backgroundContainer = new Sprite(); background = new HomeBackground();

backgroundContainer.addChild( background ); addChild( backgroundContainer );

}

override public function intro() : void {

background.intro();

introCopyAnimation = new IntroCopyAnimation();

introCopyAnimation.x = (stage.stageWidth - introCopyAnimation.width) * .5; introCopyAnimation.y = 175;

addChild( introCopyAnimation );

addEventListener( HomeEvent.ACTIVATE , activate ); addEventListener( HomeEvent.DEACTIVATE , deactivate );

background.addEventListener( HomeEvent.NAVIGATION_ACTIVATE , navigate ); background.addEventListener( HomeEvent.NAVIGATION_DEACTIVATE , navigate ); background.addEventListener( HomeEvent.NAVIGATE_TO_SECTION , navigate ); }

override public function updateLayout( width : Number , height : Number ) : void {

if (introCopyAnimation) {

introCopyAnimation.x = (stage.stageWidth - introCopyAnimation.width) * .5; introCopyAnimation.y = 175;

}

super.updateLayout( width , height );

BestFit.scaleToFill( background , width , height ); background.x = (_width - background.width) * .5; background.y = (_height - background.height) * .5;

backgroundContainer.scrollRect = new Rectangle( 0 , 0 , width , height ); }

override public function destroy() : void {

background.destroy();

removeEventListener( HomeEvent.ACTIVATE , activate ); removeEventListener( HomeEvent.DEACTIVATE , deactivate );

background.removeEventListener( HomeEvent.NAVIGATION_ACTIVATE , navigate ); background.removeEventListener( HomeEvent.NAVIGATION_DEACTIVATE , navigate ); background.removeEventListener( HomeEvent.NAVIGATE_TO_SECTION , navigate ); super.destroy();

} } }

The reason the Home section will malfunction is because the Home section overrides the parent’s operation and the requests aren’t properly forwarded to the superclass, which fulfills common behavior among all classes. This is only one subclass; but what if the developers creating the other sections make similar mistakes? The sections’

implement its own logic for the interface. The lack of criteria to meet the algorithm defined by the abstract class gets in the way of a uniform structure between the algorithm and the subclasses.

You can handle this by localizing the sequence of steps necessary for an algorithm that lets subclasses implement the code they require, while focusing only on their behaviors and not on what is expected of them by the abstract class. The abstract class must possess the appropriate logic related to the sequences expected of an algorithm, in order to fulfill the appropriate behavior. Adding the skeletal operations lets each subclass define appropriate behaviors without having to consider the when and the why.

Additionally, these abstract operations provide hooks (or callback methods), which can be overridden. This prevents possible effects on the superclass’s default functionality. To preserve the integrity of the entire algorithm in the superclass, you mark it final so no subclass can override it.

The revised ABaseSection in Listing 7-11 uses the Template Method pattern to maintain the appropriate structure of the algorithms the interface suggests. The relationship, which previously required subclasses to defer requests to the superclass, is no longer necessary because the superclass handles this independently of the subclasses. This is referred to as the Hollywood principle (“don’t call you, we’ll call you”): the subclass shouldn’t contact the superclass, because the superclass will be in touch with the subclass.

Listing 7-11. ABaseSection utilizes template methods to devise a consistency among the steps of an operation

package {

public class ABaseSection extends Sprite {

protected var _paused : Boolean; protected var _width : Number; protected var _height : Number; protected var _shell : IShell; public function ABaseSection() {

}

final public function init( shell : IShell ) : void {

_shell = shell;

_shell.addEventListener( ShellEvent.EXIT_SECTION , outro ); doLoadAssets();

}

final public function pause() : void { _paused = true; doPauseVideo(); doPauseAnimations(); doPauseTimers(); doPauseSounds(); }

final public function unPause() : void {

_paused = false doUnPauseVideo();

doUnPauseTimers(); doUnPauseSounds(); }

final public function intro() : void {

} /*

* destroy expects memory to be released. */

final public function destroy() : void {

shell.removeEventListener( ShellEvent.EXIT_SECTION , outro ); doDestroy();

}

final public function updateLayout( width : Number , height : Number ) : void {

_width = width; _height = height; doUpdateLayout(); }

final protected function outro() : void {

doOutro(); }

protected function doLoadAssets() : void {

throw new IllegalOperationError( 'doLoadAssets must be overridden' ); }

protected function doPauseVideo() : void {

throw new IllegalOperationError( 'doPauseVideo must be overridden' ); }

protected function doPauseAnimations() : void {

throw new IllegalOperationError( 'doPauseAnimations must be overridden' ); }

protected function doPauseTimers() : void {

throw new IllegalOperationError( 'doPauseTimers must be overridden' ); }

protected function doPauseSounds() : void {

throw new IllegalOperationError( 'doPauseSounds must be overridden' ); }

protected function doUnPauseVideo() : void {

throw new IllegalOperationError( 'doUnPauseVideo must be overridden' ); }

protected function doUnPauseAnimations() : void {

throw new IllegalOperationError( 'doUnPauseAnimations must be overridden' ); }

protected function doUnPauseTimers() : void {

throw new IllegalOperationError( 'doUnPauseTimers must be overridden' ); }

protected function doUnPauseSounds() : void {

throw new IllegalOperationError( 'doUnPauseSounds must be overridden' ); }

protected function doDestroy() : void {

throw new IllegalOperationError( 'doDestroy must be overridden' ); }

protected function doUpdateLayout() : void {

throw new IllegalOperationError( 'doUpdateLayout must be overridden' ); }

protected function doOutro() : void {

throw new IllegalOperationError( 'doOutro must be overridden' ); }

} }

In ActionScript 3, you can use the Template Method pattern to improve the structure of your code and localize uniform behavior. By exposing an interface that can’t be overridden, subclasses are strong armed to override only protected methods.

FAQ

Does a Template Method ever contain only one step? •

Yes, especially in AS3. This helps protect your data. Because you can’t specify virtual or abstract modifiers for methods in AS3, it isn’t clear which methods need to be overridden. This may invite methods marked as protected or public from being overridden. Thus, may encourage overriding operations among interfaces as they use the public modifier. Instead, by using the Template Method pattern, you can emphasize the method that should be overridden. Because this method is no longer part of the interface, it allows better data hiding because you can mark it protected.

This approach encourages data hiding in the class’s behaviors. The Template Method will help you to make better use of the private and protected modifiers.

Is a template method supposed to be declared as final? •

Yes. No subclass should be able to modify it, because that would interfere with the intended sequence that makes up the algorithm. Also, as in the answer to the first question, if you don’t mark the template method final, you leave the interface open to be overridden.

Related Patterns

The following patterns are related to the Template Method pattern: Factory Method

Strategy •