策略模式的完美应用 A Perfect Application of the Strategy Pattern

AQUA ICONS APPLICATIONS CHESSProblem description

In large "enterprise" systems, there are thousands of UI components displaying different  information. Opening multiple windows for containing these components is not practical. Often, you need to reuse the same space to display different UI components. In order to create an appealing UI experience, you'd like display some effects when replacing UI components in a container. You could wire the effects inline with your UI code all together, e.g,:

// replace A with B in container C

var _sequence:Sequence;
var _dissolve = new Dissolve();
_dissolve.alphaFrom = 1;
_dissolve.alphaTo = 0;
_sequence.addChild(_dissolve);

_removeAction = new SimpleRemoveAction();
_sequence.addChild(_removeAction);

_addAction = new SimpleAddAction();
_sequence.addChild(_addAction);

...

_sequence.play();

// replace E with F in container G

_sequence = new Sequence();

...

but soon you'll have long, repetitive (in certain extent) code that hard to manage.

 

Applying the strategy pattern

The strategy pattern is often used for dynamically configuring the algorithm for certain tasks. Here our task is to replace UI components while playing some effects. The difference between algorithms are on the effects.

First, we create an interface representing the replace algorithm:

package
{
    import mx.core.Container;
    import mx.core.UIComponent;
    import mx.effects.CompositeEffect;
    import mx.effects.Effect;

/**
* The strategy used for replacing UI components while playing effects.
*/
public interface IReplaceEffectStrategy {
    /**
     * Replaces the UI component in the given container while playing the effect.
     * @param container the target container
     * @param uiToBeRemoved the component to be added or null if remove only
     * @param uiToBeAdded the component to be removed or null if add only
     * @param addIndex the index that the new child should be added to or -1 if adding to the last.
     * @return the effect object, you can use this object to monitor effect starting and ending.
     */
    function replaceWithEffects(container:Container, uiToBeRemoved:UIComponent, uiToBeAdded:UIComponent, addIndex:int = -1):CompositeEffect;
    /**
     * A shortcut function equivalent to replaceWithEffects(container, null, uiToBeAdded, addIndex).
     */
    function addWithEffects(container:Container, uiToBeAdded:UIComponent, addIndex:int = -1):CompositeEffect;

    /**
     * A shortcut function equivalent to replaceWithEffects(container:Container, uiToBeRemoved, null).
     */
    function removeWithEffects(container:Container, uiToBeRemoved:UIComponent):CompositeEffect;

} // end class
} // end package

Then you create different algorithms by implementing the interface:

public class CoolReplaceEffectStrategy implements IReplaceEffectStrategy ...

public class SuperReplaceEffectStrategy implements IReplaceEffectStrategy ...

With multiple implementations, you can create a factory for easy look up of the strategies.

 

The revised code

Whenever you need to replace UI components, you simply need an instance of IReplaceEffectStrategy:

private var replaceEffectStrategy:IReplaceEffectStrategy = new CoolRotateReplaceEffect();

replaceEffectStrategy.replaceWithEffects(container, __existingContent, ui);

Note: Not only the code is cleaned up, the same strategy object can be reused. Further more, it's very easy to switch the replace effect strategy.