Design Pattern - Command

The command pattern is useful to handle direction from args in command line app. 

For example, there was an ugly code as below, which I wrote a while ago. As I simplified the code to focus on if-else statement, it may look not that bad.  But, I was struggling with managing the mapping each method with the command from argument especially when adding a new one or modifying the existing one. 

Many said there is code smell when you see a long if-else or case statement. I didn't know what was the problem until I fixed it. 

if (runOption == "all")
                {
                    await _stockPriceManager.ReadStockPriceFromWeb(stocksToRead, _appSettings.DaysToReadFromWeb);

                    // Iterate simulation
                    var averageAnnualReturn = await IterateSimulationAsync(investDay, stocksToAnalyze, _tradeOption);
                    
                }
                else if (runOption == "recalculate")
                {
                    await _stockPriceManager.ReCalculateCoefficient(stocksToAnalyze);
                }
                else if (runOption == "simulate")
                {
                    var averageAnnualReturn = await IterateSimulationAsync(investDay, stocksToAnalyze, _tradeOption);
                }
                else if (runOption.ToLower() == "readprice")
                {
                    await _stockPriceManager.ReadStockPriceFromWeb(stocksToRead, _appSettings.DaysToReadFromWeb);
                }

 

This code refactored as below using the command pattern. After refactoring, the selection condition was deligated to the concrete command object. To add a new action, it's possible just to add new command object to the availablecommands array and no need to modify the code below that. Another and actually more important benefit that I didn't show from this simple example code is that I could separate responsibility of each command under its own class. As you may imagine, most of those were inside the same class where if-else statement was. Implementing the command pattern naturally led me to implement the single responsibility principle. 

The downside of this approach is that you need to create a new 3 command class and one ICommand interface. When it's a simple program like a prototype, we might want to just use a simple if-else style. But, if there is any chance the code become a long-running one, which implies that continuous add/modification will be followed by, I would use command pattern. 

                var availableCommands = new ICommand[]
                {
                    new UpdatePrice(_stockPriceManager, stocksToRead, _appSettings.DaysToReadFromWeb),
                    new UpdateStats(_stockPriceManager, stocksToAnalyze),
                    new SimulateCommand(_stockPriceManager, stocksToAnalyze, _marketWatchRepository, investDay,
                        _tradeOption, _logger, _appSettings)
                };

                if (runOption == "all")
                {
                    availableCommands.Select(async c => await c.Execute());
                }
                else
                {
                    var command = availableCommands.Single(c => c.Name == runOption);
                    await command.Execute();
                }

 

This was possible because each command shares the same interface, ICommand. That looks like below. 

    public interface ICommand
    {
        string Name { get; }

        Task Execute();
    }

 

One of the implementations of ICommand will look like below. 

    public class UpdateStats : ICommand
    {
        public string Name => "UpdateStats";

        private readonly IStockPriceManager _stockPriceManager;
        private readonly List<string> _stocksToAnalyze;

        public UpdateStats(IStockPriceManager stockPriceManager, List<string> stocksToAnalyze)
        {
            _stockPriceManager = stockPriceManager;
            _stocksToAnalyze = stocksToAnalyze;
        }

        public async Task Execute()
        {
            await _stockPriceManager.ReCalculateCoefficient(_stocksToAnalyze);
        }
    }

The key here is that all command object share the member, Name, and method, Execute() so that these commands can be used in a standardized manner. 

To see more about the command pattern, go to https://www.dofactory.com/net/command-design-pattern

Leave a comment

Please note that we won't show your email to others, or use it for sending unwanted emails. We will only use it to render your Gravatar image and to validate you as a real person.