【问题标题】:Recommend a design pattern推荐一个设计模式
【发布时间】:2011-08-08 02:55:28
【问题描述】:

我正在处理的应用程序处理工作项。根据工作项的状态,有许多可用的操作。 “完成”“取消”“重新分配”等...

为了为操作提供功能,我目前有一个看起来像这样的界面......

public interface IActionProvider{
    public void Complete(WorkItem workItem);
    public void Cancel (WorkItem workItem);
    public void Reassign(WorkItem workItem);
}

然后根据工作项的其他细节,我有了接口的具体实现。举个例子……

public class NormalActionProvider :IActionProvider
{
    ...
}

public class UrgentActionProvider : IActionProvider
{
   ....
}

问题是,如果我想添加一个新动作,比如说......“委托”,我必须更新接口,这当然会对所有实现产生影响。

这是否违反了打开/关闭原则?你能推荐一个可能对我有帮助的设计模式或重构吗?

【问题讨论】:

    标签: c# design-patterns solid-principles


    【解决方案1】:

    看起来命令模式很合适。您可以修改/添加更多命令。命令类与主程序分离。

    public interface IActionProvider{
        public void execute(WorkItem item,ActionType actionType);
    }
    

    ActionType 代表 Complete、Cancel 等。您可以继续添加更多动作类型和插件适当的命令类。

    【讨论】:

      【解决方案2】:

      您始终可以将装饰器添加到 IActionProvider 接口(遵循Decorator design pattern)。

      【讨论】:

        【解决方案3】:

        这取决于你真正想用IActionProvider 完成什么。如果你真的想让每个实现都必须能够执行你认为重要的所有操作,那么这应该是它们实现的接口的一部分。如果提前精心规划接口,它们就可以发挥最佳效果,这样它们就不必不断地改变。

        但听起来您不一定希望所有操作都由所有提供者实施。我需要了解更多细节才能提供好的建议,但一个例子是让提供者针对一种事件总线初始化自己。他们可以订阅他们关心的那些事件,并且只针对对特定实现有意义的事件执行操作。

        【讨论】:

          【解决方案4】:

          “根据工作项的状态”,带来状态设计模式

          无论哪种方式,您都必须重构您的界面并最终破坏客户合同。

          如果我正确理解了您的问题,那么您有一个 WorkItemProcessor,其状态会根据 在 WorkItem 上发送给它。

          因此你的 WorkItemProcessor 变成了

          // Context
              public class WorkItemProcessor
              {
                  public IState CurrentState { get; set; }
          
                  public WorkItemProcessor(IState initialState)
                  {
                      CurrentState = initialState;
                  }
          
                  public void Process(WorkItem workItem)
                  {
                      CurrentState.Handle(this, workItem);
                  }
              }
          

          然后我们定义 WorkItemProcessor 可能处于的多个状态

          // State Contract
              public interface IState
              {
                  void Handle(WorkItemProcessor processor, WorkItem item);
              }
          
              // State One
              public class CompleteState : IState
              {
                  public void Handle(WorkItemProcessor processor, WorkItem item)
                  {
                      processor.CurrentState = item.CompletenessConditionHoldsTrue ? (IState) this : new CancelState();
                  }
              }
          
              // State Two
              public class CancelState : IState
              {
                  public void Handle(WorkItemProcessor processor, WorkItem item)
                  {
                      processor.CurrentState = item.CancelConditionHoldsTrue ? (IState) this : new CompleteState();
                  }
              }
          

          假设您的 WorkItem 看起来像

           // Request
              public class WorkItem
              {
                   public bool CompletenessConditionHoldsTrue { get; set; }
          
                   public bool CancelConditionHoldsTrue { get; set; }
              }
          

          把它们放在一起

          static void Main()
              {
                // Setup context in a state 
                WorkItemProcessor processor = new WorkItemProcessor(new CancelState());
          
                var workItem1 = new WorkItem {  CompletenessConditionHoldsTrue = true };
                var workItem2 = new WorkItem {  CancelConditionHoldsTrue = true };
          
                // Issue requests, which toggles state 
                processor.Process(workItem1);
                processor.Process(workItem2);
          
                Console.Read();
              }
          

          希望这能让你更接近。干杯。

          【讨论】:

            【解决方案5】:

            我也会选择命令模式。作为一种增强,您可以将它与抽象工厂方法结合起来,这样您就可以为每个命令类拥有一个工厂类,并且所有这些工厂都实现了一个通用的工厂接口。

            例如:

            // C#
            public interface ICommand { void Execute(); }
            
            public interface ICommandFactory { ICommand Create(); }
            
            public class CommandFactoryManager
            {
                private IDictionary<string, ICommandFactory> factories;
            
                public CommandFactoryManager()
                {
                    factories = new Dictionary<string, ICommandFactory>();
                }
            
                public void RegisterCommandFactory(string name, ICommandFactory factory)
                {
                    factories[name] = factory;
                }
            
                // ...
            }
            

            这样,您可以动态注册新的命令工厂。例如,您可以在运行时加载一个 DLL 并使用反射获取所有实现 ICommandFactory 接口的类,并且您有一个简单的插件系统。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-12-18
              • 1970-01-01
              相关资源
              最近更新 更多