Strategy


The STRATEGY pattern solves the problem of inverting the dependencies of the generic algorithm and the detailed implementation in a very different way. Consider once again the pattern-abusing Application problem.

Rather than placing the generic application algorithm into an abstract base class, we place it into a concrete class named ApplicationRunner. We define the abstract methods that the generic algorithm must call within an interface named Application. We derive FtoCStrategy from this interface and pass it into the ApplicationRunner. ApplicationRunner then delegates to this interface. See Figure 22-2 and Listings 22-8 through 22-10.

Figure 22-2. STRATEGY structure of the Application algorithm


It should be clear that this structure has both benefits and costs over the TEMPLATE METHOD structure. STRATEGY involves more total classes and more indirection than TEMPLATE METHOD. The delegation pointer within ApplicationRunner incurs a slightly higher cost in terms of runtime and data space than inheritance would. On the other hand, if we had many different applications to run, we could reuse the ApplicationRunner instance and pass in many different implementations of Application, thereby reducing the code space overhead.

None of these costs and benefits are overriding. In most cases, none of them matters in the slightest. In the typical case, the most worrisome is the extra class needed by the STRATEGY pattern. However, there is more to consider.

Consider an implementation of the bubble sort that uses the STRATEGY pattern. See Listings 22-11 through 22-13.

Listing 22-8. ApplicationRunner.cs

public class ApplicationRunner {   private Application itsApplication = null;   public ApplicationRunner(Application app)   {     itsApplication = app;   }   public void run()   {     itsApplication.Init();     while (!itsApplication.Done())       itsApplication.Idle();     itsApplication.Cleanup();   } }

Listing 22-9. Application.cs

public interface Application {   void Init();   void Idle();   void Cleanup();   bool Done(); }

Listing 22-10. FtoCStrategy.cs

using System; using System.IO; public class FtoCStrategy : Application {   private TextReader input;   private TextWriter output;   private bool isDone = false;   public static void Main(string[] args)   {     (new ApplicationRunner(new FtoCStrategy())).run();   }   public void Init()   {     input = Console.In;     output = Console.Out;   }   public void Idle()   {     string fahrString = input.ReadLine();     if (fahrString == null || fahrString.Length == 0)       isDone = true;     else     {       double fahr = Double.Parse(fahrString);       double celcius = 5.0/9.0*(fahr - 32);       output.WriteLine("F={0}, C={1}", fahr, celcius);     }   }   public void Cleanup()   {     output.WriteLine("ftoc exit");   }   public bool Done()   {     return isDone;   } }

Listing 22-11. BubbleSorter.cs

public class BubbleSorter {   private int operations = 0;   private int length = 0;   private SortHandler itsSortHandler = null;   public BubbleSorter(SortHandler handler)   {     itsSortHandler = handler;   } public int Sort(object array) {   itsSortHandler.SetArray(array);   length = itsSortHandler.Length();   operations = 0;   if (length <= 1)     return operations;   for (int nextToLast = length - 2;     nextToLast >= 0; nextToLast--)     for (int index = 0; index <= nextToLast; index++)     {       if (itsSortHandler.OutOfOrder(index))         itsSortHandler.Swap(index);       operations++;     }   return operations; }

Listing 22-12. SortHandler.cs

public interface SortHandler {   void Swap(int index);   bool OutOfOrder(int index);   int Length();   void SetArray(object array); }

Listing 22-13. IntSortHandler.cs

public class IntSortHandler : SortHandler {   private int[] array = null;   public void Swap(int index)   {     int temp = array[index];     array[index] = array[index + 1];     array[index + 1] = temp;   }   public void SetArray(object array)   {     this.array = (int[]) array;   }   public int Length()   {     return array.Length;   }   public bool OutOfOrder(int index)   {     return (array[index] > array[index + 1]);   } }

Note that the IntSortHandler class knows nothing whatever of the BubbleSorter, having no dependency whatever on the bubble sort implementation. This is not the case with the TEMPLATE METHOD pattern. Look back at Listing 22-6, and you can see that the IntBubbleSorter depended directly on BubbleSorter, the class that contains the bubble sort algorithm.

The TEMPLATE METHOD approach partially violates DIP. The implementation of the Swap and OutOfOrder methods depends directly on the bubble sort algorithm. The STRATEGY approach contains no such dependency. Thus, we can use the IntSortHandler with Sorter implementations other than BubbleSorter.

For example, we can create a variation of the bubble sort that terminates early if a pass through the array finds it in order. (See Figure 22-14.) QuickBubbleSorter can also use IntSortHandler or any other class derived from SortHandler.

Listing 22-14. QuickBubbleSorter.cs

public class QuickBubbleSorter {   private int operations = 0;   private int length = 0;   private SortHandler itsSortHandler = null;   public QuickBubbleSorter(SortHandler handler)   {     itsSortHandler = handler;   }   public int Sort(object array)   {     itsSortHandler.SetArray(array);     length = itsSortHandler.Length();     operations = 0;     if (length <= 1)       return operations;     bool thisPassInOrder = false;     for (int nextToLast = length-2;       nextToLast >= 0 && !thisPassInOrder; nextToLast--)     {       thisPassInOrder = true; //potenially.       for (int index = 0; index <= nextToLast; index++)       {         if (itsSortHandler.OutOfOrder(index))         {           itsSortHandler.Swap(index);           thisPassInOrder = false;         }         operations++;       }     }     return operations;   } }

Thus, the STRATEGY pattern provides one extra benefit over the TEMPLATE METHOD pattern. Whereas the TEMPLATE METHOD pattern allows a generic algorithm to manipulate many possible detailed implementations, the STRATEGY pattern, by fully conforming to DIP, additionally allows each detailed implementation to be manipulated by many different generic algorithms.




Agile Principles, Patterns, and Practices in C#
Agile Principles, Patterns, and Practices in C#
ISBN: 0131857258
EAN: 2147483647
Year: 2006
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net