Search code examples
oopdesign-patternscommand-patternopen-closed-principle

Adding new commands with Command Design Pattern


I'm having a dilemma regarding Command Design Pattern. Receiver is the interface for a class that knows how to perform operations.

In the example case given in the link, receiver class Stock Trade knows how to perform 2 operations, StockTrade#buy and StockTrade#sell. Those operations correspond to 2 existing commands, BuyStockOrder and SellStockOrder.

However, what if additional commands would need to be added, for example FooStockOrder and BarStockOrder? Then, the Receiver interface would have to be changed (and all existing implementations), therefore violating the Open-Closed principle.

How to tackle that problem in a way that Receiver is (almost) never changed?


Solution

  • You are forgetting your S.O.L.I.D principles, specifically Dependancy Inversion

    Don't define your interface in a way that it specifically expects a concrete implimentation like, BuyStockOrder or SellStockOrder.

    Rather define your interface to expect an abstraction like CommandRequest, you will then send in concrete implimentations of the CommandRequest without ever having to change your interface.

    Then your Receiver implimentation can decide what to do with it, of course the correct way to deal with this would be using a Strategy Pattern but I'll leave that up to you.

    Here is some code as a simple example:

    public class Receiver : IReceiver
    {
        public CommandResult Process(CommandRequest request)
        {
            // Check the type of request and handle appropriately (or send to who can handle it)
    
            return new Result();
        }
    }
    
    public interface IReceiver
    {
        CommandResult Process(CommandRequest request);
    }
    
    public abstract class CommandResult
    {
        public bool Successful { get; set; }
    }
    
    public abstract class CommandRequest
    {
    }
    
    public class Result : CommandResult
    {
    }
    
    public class BuyStock : CommandRequest
    {
        public string Name { get; set; }
        public decimal Value { get; set; }
    }
    
    public class SellStock : CommandRequest
    {
        public string Name { get; set; }
        public decimal Value { get; set; }
    }
    
    internal class Program
    {
        private static void Main(string[] args)
        {
            var receiver = new Receiver();
            var result = receiver.Process(new SellStock { Name = "PAYPL", Value = 100.20m });
    
            Console.WriteLine(result.Successful ? "Yay!" : "Boo!");
        }
    }