介绍"强制性"接口实现者的方法

时间:2018-04-06 04:22:35

标签: c# oop design-patterns interface

当前的实现几乎与简单的策略设计模式一致。有多个步骤要执行,这些步骤将由以下界面调用:

public interface ICommandStep
{
    void Execute(CommandParameters cParams);
    string StepName { get; }
}

示例实现者:

public class Step1 : ICommandStep {
    public string StepName => "Initial Step";
    public void Execute(CommandParameters cParams) {
        // instructions
    }
}

到目前为止,有很多不同的类实现了这个接口,我想确保所有这些类都有一个执行前后步骤。例如log state,params,StepName start和end。

我如何介绍一种方法,使protected virtual void PreExecuteprotected virtual void PostExecute方法具有通用(可覆盖)逻辑,并确保始终按此顺序调用该方法:

1. PreExecute
2. Execute
3. PostExecute

最好不要在实现者类中更改Execute方法。

可以引入抽象类。

3 个答案:

答案 0 :(得分:3)

您可以按所需顺序声明基类并定义可覆盖的方法:

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
    }
}

答案 1 :(得分:0)

向界面添加新方法必然意味着打破现有的实现。如果实现是可选的,那么您应该使用abstract类而不是interface

通过添加新接口并在扩展方法中添加运行时类型检查,存在另一种选择,如下所示:

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 );
        }
    }
}

用法:

ICommandStep[] steps = ...
foreach( ICommandStep step in steps )
{
    step.Execute2( cmdParams );
}

答案 2 :(得分:0)

我可以想到2个解决方案:

  1. Backs提出的模板方法模式。
  2. 装饰图案与工厂一起。当你想在调用其他对象之前/之后执行一些代码时,装饰器模式很棒。工厂将确保每个创建的对象都将被装饰。
  3. 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();
            }
        }
    }
    

    此解决方案有几个优点:

    • 您可以使用许多装饰器包装您的步骤,即您可以在装饰器中包装步骤以显示它的名称,并将这两个包装在另一个显示参数的装饰器中
    • 您不必修改您的步骤,因为所有额外的工作都由装饰员处理