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?
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!");
}
}