Sunday 19 December 2010

Strategy Pattern

"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."


In the code example below Pets are sorted according to different sorting strategies. The client (main()) sorts the list of PetRatings using the static SortPets() method of the Criteria class. If you imagine that either the Criteria class is located in a separate assembly or the sortStrategy string that is passed as the second parameter to that method is read from configuration instead of being hardcoded as shown in the example, you can see how client code is able to remain isolated (i.e. it wouldn't need to be rebuilt) from changes to the underlying strategy.
using System;
using System.Collections.Generic;
 
namespace Strategy
{
    class Program
    {
        static void Main(string[] args)
        {
            PetRatingList pets = new PetRatingList();
 
            pets.Add( new PetRating
            { Name = "Jess", Cuteness = 3, Obedience = 3, Slathering = 1
            });
            pets.Add( new PetRating
            { Name = "Royston", Cuteness = 2, Obedience = 2, Slathering = 3
            });
            pets.Add( new PetRating
            { Name = "Dot", Cuteness = 1, Obedience = 1, Slathering = 2
            });
 
            Console.WriteLine("\n========== CUTENESS ==========");
            Criteria.SortPets(pets, "Strategy.SortByCuteness");
            foreach (PetRating pet in pets)
            {
                Console.WriteLine("Name = {0}, Cuteness = {1}",
                    pet.Name, pet.Cuteness);
            }
 
            Console.WriteLine("\n========== SLATHER ==========");
            Criteria.SortPets(pets, "Strategy.SortBySlathering");
            foreach (PetRating pet in pets)
            {
                Console.WriteLine("Name = {0}, Foam = {1}",
                    pet.Name, pet.Slathering);
            }
 
            Console.WriteLine("\n========== OBEDIENCE ==========");
            Criteria.SortPets(pets, "Strategy.SortByObedience");
            foreach (PetRating pet in pets)
            {
                Console.WriteLine("Name = {0}, Obedience = {1}",
                    pet.Name, pet.Obedience);
            }
 
            Console.ReadLine();
         }
    }
 
    public struct PetRating
    {
        public string Name { getset; }
        public int Cuteness { getset; }
        public int Slathering { getset; }
        public int Obedience { getset; }
    }
 
    public class PetRatingList : List<PetRating>
    {     
    }
 
    // Context class - calls strategy
    public class Criteria
    {
        public static void SortPets(
        PetRatingList pets, string sortStrategy)
        {
            IPetSort sorter =
            Activator.CreateInstance(Type.GetType(sortStrategy))
            as IPetSort;
          
            sorter.Sort(pets);
        }
    }
 
    // Strategy class -defines common interface for concrete
    // strategies

    public interface IPetSort
    {
        void Sort(PetRatingList pets);     
    }
 
    // Concrete strategy 1
    public class SortByCuteness : IPetSort
    {
        private int CutenessSorter(PetRating pet1, PetRating pet2)
        {
            return pet2.Cuteness.CompareTo(pet1.Cuteness);
        }
      
        public override void Sort(PetRatingList pets)
        {
            pets.Sort(new Comparison<PetRating>(CutenessSorter));
        }
    }
 
    // Concrete strategy 2
    public class SortByObedience : IPetSort
    {
        private int ObedienceSorter(PetRating pet1, PetRating pet2)
        {
            return pet2.Obedience.CompareTo(pet1.Obedience);
        }
      
        public override void Sort(PetRatingList pets)
        {
            pets.Sort(new Comparison<PetRating>(ObedienceSorter));
        }
    }
 
    // Concrete strategy 3
    public class SortBySlathering : IPetSort
    {
        private int SlatheringSorter(PetRating pet1, PetRating pet2)
        {
            return pet2.Slathering.CompareTo(pet1.Slathering);
        }
   
        public override void Sort(PetRatingList pets)
        {
            pets.Sort(new Comparison<PetRating>(SlatheringSorter));
        }
    }
}
Outputs:

========== CUTENESS ==========
Name = Jess, Cuteness = 3
Name = Royston, Cuteness = 2
Name = Dot, Cuteness = 1

========== SLATHER ==========
Name = Royston, Foam = 3
Name = Dot, Foam = 2
Name = Jess, Foam = 1

========== OBEDIENCE ==========
Name = Jess, Obedience = 3
Name = Royston, Obedience = 2
Name = Dot, Obedience = 1


No comments:

Post a Comment