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