Saturday, September 15, 2012

C# - Dynamic Factory for Commands

Factories are a useful and important mechanism in programming.  I have written many of them in Java and recently created one for Commands in C#.  There are many ways that a factory can work to provide you with implementations you request, but the ideal one handles it dynamically.  Once a factory is built, you really shouldn't have to manually add the implementations to a map... this is error prone and harder to maintain.  Ideally, you would just be able to add a new class, recompile, and run.  Even better, if done properly, this can be done by simply adding the class to a folder and it is automatically picked up.  For this post, I have thrown together a very simplified example of a factory that automatically loads the classes at runtime and are obtainable through a command name.  This example serves as the basis for a command line interpreter where the command passed in is used to lookup the implementation.

To start with, let's create an interface for Commands. For this example, I am using a base namespace of BlogSandox.
namespace BlogSandbox.command
{
    public interface ICommand
    {
        string CommandName { get; }
        string Execute(string input);
    }
}
All Commands will implement this interface and provide a command name (this is used to map the command to its instance).  To simplify all implementations, we will provide an abstract base class.
namespace BlogSandbox.command
{
    public abstract class BaseCommand : ICommand
    {
        private readonly string _commandName;

        protected BaseCommand(string commandName)
        {
            this._commandName = commandName;
        }

        public string CommandName
        {
            get { return _commandName; }
        }

        public abstract string Execute(string input);
    }
}
Now let's create a couple example Commands. Admittedly, these are pretty useless, but should help get the point across.
namespace BlogSandbox.command
{
    class EchoCommand : BaseCommand
    {        
        public const string NAME = "EchoCommand";
        public EchoCommand()
            : base(NAME)
        {
        }

        public override string Execute(string input)
        {
            return input;
        }
    }
}

namespace BlogSandbox.command
{
    class GreetCommand : BaseCommand
    {
        public const string NAME = "GreetCommand";
        public GreetCommand()
            : base(NAME)
        {
        }

        public override string Execute(string input)
        {
            return "Hello " + input + "!";
        }
    }
}
Now for the command factory.  This factory is going to register each instance using the "CommandName" provided by the implementing classes.  While this example doesn't have a "collision" detection (e.g. when 2 commands have the same name), it is not a bad idea to add one if you will be dealing with a lot of commands and especially if you will be making them pluggable.

namespace BlogSandbox.command
{
    public class CommandFactory
    {
        private readonly Dictionary _commandImplementations;
        private static volatile CommandFactory _commandFactoryInstance;
        private static object _syncRoot = new object();

        private CommandFactory()
        {
            _commandImplementations = new Dictionary();
            Initialize();
        }

        public static CommandFactory Instance
        {
            get
            {
                if (_commandFactoryInstance == null)
                {
                    lock (_syncRoot)
                    {
                        _commandFactoryInstance = new CommandFactory();
                    }
                }
                return _commandFactoryInstance;
            }
        }

        public ICommand GetCommand(string commandName)
        {
            return _commandImplementations[commandName.ToLower()];
        }

        private void Initialize()
        {
            // 1. get assembly
            Assembly asm = Assembly.GetCallingAssembly();

            // 2. get the list of all types in this assembly and iterate
            foreach (Type type in asm.GetTypes())
            {
                // we want the non-abstract implementations of ICommand
                if (type.IsClass && !type.IsAbstract)
                {
                    Type iCommand = 
                        type.GetInterface(
                          "BlogSandbox.command.ICommand");
                    if (iCommand != null)
                    {
                        // create an instance
                        object inst = 
                            asm.CreateInstance(type.FullName, true, 
                             BindingFlags.CreateInstance, null, null,
                             null, null);
                        if (inst != null)
                        {
                            ICommand commandInst = (ICommand)inst;
                            // make it case insensitive
                            string key = 
                                commandInst.CommandName.ToLower();
                            _commandImplementations.Add(
                                key, commandInst);
                        }
                        else
                        {
                            string errMsg =
                                "CommandFactory.Initialize(): Unable " +
                                "to properly initialize " +
                                "CommandFactory - there was a " +
                                "problem instantiating the class " +
                                type.FullName;
                            throw new Exception(errMsg);
                        }
                    }
                }
            }
        }
    }
}
The main thing to catch here is the use of the "Assembly" to find the classes and then load the ones that are of the appropriate type (e.g. non-abstract class that implements our command interface ICommand). To see this working, here is a simple main that you can add for a Console Application.  The sleep at the bottom keeps the window open long enough to see the results... of course, you could use a logger or other application type that doesn't close up at completion.
namespace BlogSandbox
{
    class Program
    {
        static void Main(string[] args)
        {
            CommandFactory factory = CommandFactory.Instance;
            Console.WriteLine("Echo Command: " + 
                factory.GetCommand(EchoCommand.NAME).
                    Execute("Repeat me"));
            Console.WriteLine("Greet Command: " + 
                factory.GetCommand(GreetCommand.NAME).
                    Execute("Paul"));
            Thread.Sleep(20000);
        }
    }
}
These commands are clearly very simplified, but I hope that you can see how this can be applied in your own use case.  In a full implementation that I wrote for work, the BaseCommand method had protected methods for performing validations on the commands, returned a CommandResponse object where errors could be added in a list or a successful response could be provided (such as returning a result of the action). Additionally, for building my commands I used a fantastic command line parser library.  I highly recommend this library as it really simplified taking in command arguments.

This simplified example really only touches on the factory for commands, but doesn't really discuss a good way to implement the commands, nor how to parse a command line.  In a future post, I will explore the Command Line Parser Library I used and try to build from this example so that you can see more of the whole picture.  I hope this helps someone out there!  As always, feel free to use my code - while not necessary, attribution in the form of a link back to this post is always appreciated!

No comments:

Post a Comment

Followers