【问题标题】:Alternative method for Switch StatementsSwitch 语句的替代方法
【发布时间】:2018-03-02 01:37:56
【问题描述】:

根据一些文章:

理论说这很容易,但实际上这通常很困难。 没关系。让我们深入了解这件事的核心。

代码如下所示:

    public class SomeType
    {
        // Some properties.
    }

    public enum SomeTrigger
    {
        Loaded,
        Initial,
        Timer,
        Final
    }


    public class SomeBaseObject
    {
        // 
        // I am not allowed to change this class. It is not mine.
        //

        protected Dictionary<string, SomeType> Input;
        protected Dictionary<string, SomeType> Output;


        // Timer is evaluated every 2 sec.
        public virtual void Execute(SomeTrigger trigger, string value)
        {
            switch (trigger)
            {
                case SomeTrigger.Loaded:
                    break;
                case SomeTrigger.Initial:
                    break;
                case SomeTrigger.Timer:
                    break;
                case SomeTrigger.Final:
                    break;
                default:
                    break;
            }
        }

        // The rest of code...
    }

还有我想改进的课程:

public class SomeSpecificObject : SomeBaseObject
    {
        private bool isInitializationCompleted;
        private string connection;

        public override void Execute(SomeTrigger trigger, string value)
        {
            switch (trigger)
            {
                case SomeTrigger.Loaded:
                    this.OnLoaded();
                    break;
                case SomeTrigger.Initial:
                    this.OnInitial();
                    break;
                case SomeTrigger.Timer:
                    this.OnTimer();
                    break;
                case SomeTrigger.Final:
                    this.OnFinal(value);
                    break;
                default:
                    break;
            }
        }

        private void OnLoaded()
        {
            // Read Input and Output collection.
        }

        private void OnInitial()
        {
            // Initialization (connect to the server).
            // Bla bla bla
            this.connection = //Value from the plug-in;
            this.isInitializationCompleted = true;

        }

        private void OnTimer()
        {
            if (isInitializationCompleted)
            {
                // Do something
                // Connection is using here.
                // Calculate values on a Input collection, etc.
            }
        }

        private void OnFinal(string value)
        {
            if (isInitializationCompleted)
            {
                // something with "value"
                // Connection is using here.
                // Clear state.
            }
            else
            {
                // Connection is using here.
                // Cancel inistialization
            }
        }
    }

我该怎么办?每个字段都在使用但每个触发器。此外,一种情况有点具体(OnFinalMethod 需要参数)。根据以上文章,我尝试重构这段代码,但没有成功。

My attempts to apply some tips: 

    public interface ITrigger
    {
        void Execute();
    }

    public class LoadedTrigger : ITrigger
    {
        public void Execute()
        {
            throw new NotImplementedException();
        }
    }

    //
    // Exactly the same class for the rest cases.
    //
    public class TriggerHandler
    {
        private Dictionary<SomeTrigger, ITrigger> triggerDictionary;

        public TriggerHandler()
        {

            triggerDictionary.Add(SomeTrigger.Loaded, new InitialTrigger());
    // and for the rest

        }

        public void HandleTrigger(SomeTrigger trigger)
        {
            triggerDictionary[trigger].Execute();
        }
    }

对象应该如何相互通信?例如。 TimerTrigger 对象需要知道启动是否成功。我应该如何处理特殊情况对象?

有什么想法吗? :)

【问题讨论】:

  • 不清楚您要解决什么问题,但一种可能有效的设计模式是Strategy Pattern。查看示例 herehere
  • @NightOwl888 - 我已经简化了这个例子。在实际项目中,switch 语句有 8 种情况,只有一个类处理所有这些行为。这个类真的很大,我认为将一些功能委托给单独的类是个好主意。
  • 可能是访问者模式的双重分派,但你需要重构你的代码。

标签: c# design-patterns architecture


【解决方案1】:

首先,switch 语句一般没有问题。只是在代码中使用它会产生一些隐含的结构限制。如果其中一个限制与代码的演变方式相矛盾,换句话说,如果需求以某种方式发生变化,导致切换逻辑变得过时,那么继续在代码中包含它会导致技术债务。

从上面的代码不清楚它的要求是什么。例如,为什么首先需要有 Execute 方法?可以直接用Loaded()、Initial()等方法替换吗?这显然会消除开关。

另一种方法是使用事件对象而不是枚举并使用方法重载:Execute(LoadedTrigger ...), ..., Execute(FinalTrigger ..., ...)。

如果您想在运行时添加处理程序(是吗?),使用字典的方法很好,但缺少的部分是检查触发器类型并调用不同的执行方法:

var t = triggerDictionary[trigger];
if (t is SimpleTrigger st) st.Execute();
else if (t is AdvancedTrigger at) at.Execute(value);

或者,为触发器的执行方法添加值。但如果没有这个要求,又何必费心呢?

例如TimerTrigger 对象需要知道启动成功。

你可能想要一个状态机。 switch 是为简单状态机编写转换代码的常用方法。

【讨论】:

    【解决方案2】:

    作为一种方法,您可以使用template method。使用这种模式,您的代码将如下所示:

    public class SomeType
    {
        // Some properties.
    }
    
    public enum SomeTrigger
    {
        Loaded,
        Initial,
        Timer,
        Final
    }
    
    public abstract class SomeBaseObject
    {
        // 
        // I am not allowed to change this class. It is not mine.
        //
    
        protected Dictionary<string, SomeType> Input;
        protected Dictionary<string, SomeType> Output;
    
    
        // Timer is evaluated every 2 sec.
        public void Execute(SomeTrigger trigger, string value)
        {
            switch (trigger)
            {
                case SomeTrigger.Loaded:
                    OnLoaded();
                    break;
                case SomeTrigger.Initial:
                    OnInitial();
                    break;
                case SomeTrigger.Timer:
                    OnTimer();
                    break;
                case SomeTrigger.Final:
                    OnFinal(value);
                    break;
                default:
                    break;
            }
        }
    
        protected abstract void OnLoaded();
    
        protected abstract void OnInitial();
    
        protected abstract void OnTimer();
    
        protected abstract void OnFinal(string value);
    }
    
    public class SomeSpecificObject : SomeBaseObject
    {
        private bool isInitializationCompleted;
        private string connection;
    
    
    
        protected override void OnLoaded()
        {
            // Read Input and Output collection.
        }
    
        protected override void OnInitial()
        {
            // Initialization (connect to the server).
            // Bla bla bla
            this.connection = "";//Value from the plug-in;
            isInitializationCompleted = true;
    
        }
    
        protected override void OnTimer()
        {
            if (isInitializationCompleted)
            {
                // Do something
                // Connection is using here.
                // Calculate values on a Input collection, etc.
            }
        }
    
        protected override void OnFinal(string value)
        {
            if (isInitializationCompleted)
            {
                // something with "value"
                // Connection is using here.
                // Clear state.
            }
            else
            {
                // Connection is using here.
                // Cancel inistialization
            }
        }
    }
    

    如果需要,可以提供 OnXXX 方法的默认实现并将其标记为虚拟

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      • 2022-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-22
      相关资源
      最近更新 更多