Strategy Pattern

| Comments

Strategy Pattern is my favorite one, and why? because it’s so simple for implamentation; it’s based on polymorphism), an the basic idea is to encapsulate the code that you think is gonna change.

for example let’s say that you have and Encryptor Class:

package 
{

    /**
     * ...
     * @author Raúl Uranga
     */
    public class EncryptorManager
    {
    /**
    * Encodes a base64 string.
    */
    public function encodeMessage(src:String):String {
        var i:Number = 0;
        var output:String = new String("");
        var chr1:Number, chr2:Number, chr3:Number;
        var enc1:Number, enc2:Number, enc3:Number, enc4:Number;
        while (i < src.length) {
            chr1 = src.charCodeAt(i++);
            chr2 = src.charCodeAt(i++);
            chr3 = src.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if(isNaN(chr2)) enc3 = enc4 = 64;
            else if(isNaN(chr3)) enc4 = 64;
            output += base64chars.charAt(enc1)+base64chars.charAt(enc2);
            output += base64chars.charAt(enc3)+base64chars.charAt(enc4)
        }
        return output;
    }

    /**
    * Decodes a base64 string.
    */
    public function decodeMessage(src:String):String {
        var i:Number = 0;
        var output:String = new String("");
        var chr1:Number, chr2:Number, chr3:Number;
        var enc1:Number, enc2:Number, enc3:Number, enc4:Number;
        while (i < src.length) {
            enc1 = base64chars.indexOf(src.charAt(i++));
            enc2 = base64chars.indexOf(src.charAt(i++));
            enc3 = base64chars.indexOf(src.charAt(i++));
            enc4 = base64chars.indexOf(src.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output += String.fromCharCode(chr1);
            if (enc3 != 64) output = output+String.fromCharCode(chr2);
            if (enc4 != 64) output = output+String.fromCharCode(chr3);
        }
        return output;
    }   
    }
}

usage:

var customEncryptor:EncryptorManager= new EncryptorManager();
var encodedMessage:String = customEncryptor.encodeMessage("my secret message hehehe");

trace(encodedMessage);
trace("message: ", customEncryptor.decodeMessage(encodedMessage) );

so, you can see here that the Encryptor Manager uses Base64 for encode and decode messages but, the client with a smile in his face, is telling you that the application no longer use Base64 and it needs to change it…….. well, the first thing that comes to your head is: “ok, i just need to change this two methods and i can continue learning salsa with AS3” XD, but what if it needs to change again and again??

we are a lazy but smart ones developers =D so we are going to implement the Strategy Pattern

first we create an Interface that would be implemented by our custom encryptors

package 
{
    public interface IEncryptor
    {
        public function encode(src:String):String;
        public function decode(src:String):String;
    }
}

then we encapsulate the code that’s is going to change in another class

Base64

package 
{
    public class Base64Encryptor implements IEncryptor
    {

            public function encode(src:String):String {
             // Base 64 implementation code goes here
            }

            public function decode(src:String):String {
             // Base 64 implementation code goes here
            }   
        }
}       

LZW

package 
{
    public class LZWEncryptor implements IEncryptor
    {

    public function encode(src:String):String {

         //  LZW implementation code goes here
    }

    public function decode(src:String):String {
        //  LZW implementation code goes here
    }
        }
}  

MD5

package 
{
    public class MD5Encryptor implements IEncryptor
    {

            public function encode(src:String):String {
             // MD5  implementation code goes here
            }

            public function decode(src:String):String {
             // MD5  implementation code goes here
            }   
        }
}       

now we need to change a little bit our EncryptorClass to manage these new encryptors

package 
{
    public var encrytor:IEncryptor;

    public class EncryptorClass (encrytor:IEncryptor = new NullEncryptor())
    {
        this.encrytor = encrytor;
    }

    public function encodeMessage(src:String):String {
        return encrytor.encode(src); // this is where the magic happends!!
    }


    public static function decodeMessage(src:String):String {
        return encrytor.decode (src); // this is where the magic happends!!
    }
}

and here it goes the new implementation

var _base64encry:IEncryptor = new Base64Encryptor();

var customEncryptor:EncryptorManager= new EncryptorManager();
customEncryptor.encrytor = _base64encry;
var encodedMessage:String = customEncryptor.encodeMessage("my secret message hehehe");
var decodedMessage = customEncryptor.decodeMessage(encodedMessage);

trace("Encoded Message : ", encodedMessage );
trace("Decoded Message : ", decodedMessage );
.
.
.
//now we can switch the encrytor algorithm just creating new Encrytor instances
customEncryptor.encrytor = new LZWEncryptor() ;

encodedMessage = customEncryptor.encodeMessage("my secret message hehehe");
decodedMessage = customEncryptor.decodeMessage(encodedMessage);

trace("Encoded Message : ", encodedMessage );
trace("Decoded Message : ", decodedMessage );

you can now create new Encrytor instances by just implementing the IEncrytor interface, and switch them with out a change in the EncryptorManager ClassXD.

Comments