Reputation: 8276
The current implementation is pretty much aligns with a simple Strategy Design Pattern. There are multiple steps to be executed and these will be invoked provided by the following interface:
public interface ICommandStep
{
void Execute(CommandParameters cParams);
string StepName { get; }
}
Example implementor:
public class Step1 : ICommandStep {
public string StepName => "Initial Step";
public void Execute(CommandParameters cParams) {
// instructions
}
}
By now there many different classes implementing this interface, and I want to ensure all of them will have a pre and post step to execution. For example log state, params, StepName start and end.
How could I introduce a way to have protected virtual void PreExecute
and protected virtual void PostExecute
methods with common (overridable) logic and to make sure the method will always be invoked in this order:
1. PreExecute
2. Execute
3. PostExecute
Preferably without changing the Execute method in the implementor classes.
Introducing abstract class is possible.
Upvotes: 1
Views: 89
Reputation: 893
I can think of 2 solutions:
Example implementation of 2.:
using System;
namespace ConsoleApplication6
{
/// <summary>
/// This is the component in Decorator Pattern
/// </summary>
public interface ICommandStep
{
void Execute(String cParams);
string StepName { get; }
}
/// <summary>
/// This is the concrete component in Decorator Pattern
/// </summary>
public class ConcreteStep1 : ICommandStep
{
public string StepName
{
get
{
return "1";
}
}
public void Execute(string cParams)
{
Console.WriteLine($"STEP {StepName}: EXECUTE");
}
}
/// <summary>
/// This is the decorator in Decorator Pattern
/// </summary>
public abstract class StepDecorator : ICommandStep
{
protected ICommandStep _commandStep;
public abstract string StepName
{
get;
}
public StepDecorator(ICommandStep commandStep)
{
this._commandStep = commandStep;
}
public abstract void Execute(string cParams);
}
/// <summary>
/// This is the concrete decorator in Decorator Pattern
/// </summary>
public class ConcreteStepDecorator : StepDecorator
{
public ConcreteStepDecorator(ICommandStep commandStep) : base(commandStep) { }
public override string StepName
{
get
{
return _commandStep.StepName;
}
}
public override void Execute(string cParams)
{
// You can do whatever you want before / after execution of command
Console.WriteLine($"STEP {_commandStep.StepName}: PRE EXECUTE");
_commandStep.Execute(cParams);
Console.WriteLine($"STEP {_commandStep.StepName}: POST EXECUTE");
}
}
/// <summary>
/// This is a Simple Factory. You encapsulate here creation of ICommandStep, so that it will always be decorated
/// </summary>
public class SimpleStepFactory
{
public ICommandStep createStep()
{
return new ConcreteStepDecorator(new ConcreteStep1());
}
}
class Program
{
static void Main(string[] args)
{
var step = new SimpleStepFactory().createStep();
step.Execute("params");
Console.ReadLine();
}
}
}
This solution has couple of advantages:
Upvotes: 0
Reputation: 155400
Adding new methods to the interface will necessarily mean breaking the existing implementations. If implementation is optional then you should use an abstract
class instead of an interface
.
An alternative exists by adding a new interface and adding a runtime type-check in an extension method, like so:
public interface ICommandStep
{
void Execute(CommandParameters cParams);
string StepName { get; }
}
public interface ICommandStep2 : ICommandStep
{
void PreExecute()
void PostExecute()
}
public static class Extensions
{
public static void Execute2(this ICommandStep step, CommandParameters cParams)
{
if( step is ICommandStep2 step2 )
{
step2.PreExecute();
step2.Execute( cParams );
step2.PostExecute();
}
else
{
step2.Execute( cParams );
}
}
}
Usage:
ICommandStep[] steps = ...
foreach( ICommandStep step in steps )
{
step.Execute2( cmdParams );
}
Upvotes: 0
Reputation: 24913
You can declare base class and define overridable methods in needed order:
public interface ICommandStep
{
void Execute(CommandParameters cParams);
string StepName { get; }
}
public abstract class CommandBase : ICommandStep
{
public void Execute(CommandParameters cParams)
{
PreExecute();
ExecuteInternal(cParams);
PostExecute();
}
protected virtual void PostExecute()
{
}
protected virtual void ExecuteInternal(CommandParameters cParams)
{
}
protected virtual void PreExecute()
{
}
public abstract string StepName { get; }
}
public class Step1 : CommandBase
{
public override string StepName => "Initial Step";
protected override void ExecuteInternal(object cParams)
{
// instructions
}
}
Upvotes: 3