Search code examples
c#design-patternsstrategy-pattern

Different algorithm for different inputs


In the application I am developing I am facing a situation; I want to know if there is a design pattern for this. It's as follows

  1. User is presented on a web interface with different algorithms for a process
  2. User selection is stored in the database.
  3. Now, the application should perform computations differently depending on the algorithm selected.

What is a good strategy to implement this? Right now what we are doing is -

  1. Have a reference DB table which has all the algorithm types and the corresponding class names in code (eg. If quick sort algorithm then we store QuickSort). This has to be manually updated everytime a new algo comes
  2. In the code, get the algorithm type and use reflection to instantiate the appropriate algorithm type. In C# we use code similar to below

    System.Reflection.Assembly types = System.Reflection.Assembly.LoadFile(System.Reflection.Assembly.GetExecutingAssembly().Location.ToString());
    foreach (Type t in types) if (t.Name==classname) createinstanceof(t) //classnames is the list of all the class types that is loaded from reference table in DB.

My gut feeling is there should be a simpler/better way to do this as it seems a very standard problem. I know the strategy pattern - but what I want is to simplify and possibly remove manual tasks.


Solution

  • You can use Interface + Reflection to avoid storing algorithm names in database.

    Create an Interface IMySortingAlgorithms as,

    public interface IMySortingAlgorithms
        {
            string Name { get; }
            string[] Sort(string[] input);
        }
    

    Now, write a Factory that uses reflection to get the sorting algorithm.

    public static class MyAlgoFactory
    {
        private static Dictionary<string, IMySortingAlgorithms> m_dict;
    
        /// <summary>
        /// For all the assmeblies in the current application domain,
        /// Get me the object of all the Types that implement IMySortingAlgorithms
        /// </summary>
        static MyAlgoFactory()
        {
            var type = typeof(IMySortingAlgorithms);
            m_dict = AppDomain.CurrentDomain.GetAssemblies().
                SelectMany(s => s.GetTypes()).
                Where(p => {return type.IsAssignableFrom(p) && p != type;}).
                Select(t=> Activator.CreateInstance(t) as IMySortingAlgorithms).
                ToDictionary(i=> i.Name);
        }
    
        public static IMySortingAlgorithms GetSortingAlgo(string name)
        {
            return m_dict[name];
        }
    }
    

    All your sorting algorithms can now implement this interface.

     public class MySortingAlgo1 : IMySortingAlgorithms
        {
            #region IMySortingAlgorithms Members
    
            public string Name
            {
                get { return "MySortingAlgo1"; }
            }
    
            public string[] Sort(string[] input)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    

    This way you need not add the class names to database whenever you create a new class for sorting.

    Following is the non-Linq version of MyAlgoFactory

        /// <summary>
        /// For all the assmeblies in the current application domain,
        /// Get me the object of all the Types that implement IMySortingAlgorithms
        /// </summary>
       static MyAlgoFactory()
            {
                m_dict = new Dictionary<string, IMySortingAlgorithms>();
                var type = typeof(IMySortingAlgorithms);
                foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
                {
                    foreach (Type p in asm.GetTypes())
                    {
                        if (type.IsAssignableFrom(p) && p != type)
                        {
                            IMySortingAlgorithms algo = Activator.CreateInstance(p)
                                  as IMySortingAlgorithms;
                            m_dict[algo.Name] = algo;
                        }
                    }
                }
            }