I'm working on a feature where I need to send analytics data whenever a command is executed — for example, whether the command was triggered via a hotkey or a UI button. However, I want to achieve this without modifying the existing command interface.
I’m using a decorator pattern to wrap command execution and insert analytics logic. But I’m not sure how to pass extra context (like trigger source) into the decorator without changing the method signature of the interface.
Here’s a simplified version of my code:
- An interface that defines a command capable of executing an action:
public interface IActionCommand
{
void ExeciteAction(ActionModel actionModel);
}
- ActionModel can contain anything related to the action (parameters, metadata, etc.)
- A base decorator for IActionCommand:
public abstract class ActionCommandDecorator : IActionCommand
{
protected IActionCommand Decorator { get; }
public ActionCommandDecorator(IActionCommand decorator)
{
Decorator = decorator;
}
public abstract void ExeciteAction(ActionModel actionModel);
}
- A concrete implementation of IActionCommand. It's the actual logic for performing some action.
public class AnyActiomCommand : IActionCommand
{
public void ExeciteAction(ActionModel actionModel)
{
//Do action
}
}
- A decorator that wraps the command to track analytics:
public class AnyActiomCommandAnalyticsDecorator : ActionCommandDecorator
{
private AnaliticaService _analiticaService = new AnaliticaService();
public AnyActiomCommandAnaliticDecorator(IActionCommand decorator) : base(decorator)
{
}
public override void ExeciteAction(ActionModel actionModel)
{
_analiticaService.SendData(new AnalyticsModel());
Decorator.ExeciteAction(actionModel);
}
}
- Analytics model can contain anything
The problem: I don’t want to modify the IActionCommand interface to include metadata or a parameter like Execute(ActionModel source , AnalyticsModel analyticsorce). I want to keep the interface clean.
Question: What is the best way to pass additional context like the source of command execution to the decorator without changing the base interface or command classes?
ActionModelis already non-compliant. Obviously you're not obligated to comply with any GoF pattern; but calling this interface a Command is confusing (and contradictory) for those who are familiar with the famous patterns. I would recommend naming the interface something else.ExeciteAction, that would keep the interface in place. However without seeing all the code that's just a guess.