代码之家  ›  专栏  ›  技术社区  ›  curious-cat

避免处理程序函数的长开关/if[关闭]

  •  4
  • curious-cat  · 技术社区  · 7 年前

    我发现我经常会写这样的代码:

    class SomeClass
    {
        HandleOtherClass(OtherClass otherClass)
        {
            switch (otherClass.State)
            {
                case OtherClassState.Unstarted:
                    this.HandleUnstarted(situation);
                    break;
                case OtherClassState.New:
                    this.HandleNew(situation);
                    break;
                case OtherClassState.Ongoing:
                    this.HandleOngoing(situation);
                    break;
                case OtherClassState.Stale:
                    this.HandleStale(situation);
                    break;
                case OtherClassState.Complete:
                    this.HandleComplete(situation);
                    break;
                default:
                    throw new NotImplementedException();
                    break;
            }
        }
    }
    

    它可以工作,但似乎我遗漏了一些可以使代码更易于维护的模式。我通常在oo long的if/switch语句中读到这一点。我考虑过字典,但它只是稍微移动代码,而没有真正改变体系结构。我该如何更好地处理这个问题?

    2 回复  |  直到 7 年前
        1
  •  1
  •   Stefan    7 年前

    当你想扩展案例时,特别是当你的同事没有上过逻辑设计课程时,必须考虑到一些额外的复杂性:

    HandleOtherClass(OtherClass otherClass)
    {
        switch (otherClass.State)
        {
            case OtherClassState.Unstarted:
                // this can become quite complicated.
                switch(yetAnotherState)
                { 
                     case 1:
                         //do stuff
                         break
                     case 2:
                         //etc
                         break;
                }
                break;
            case OtherClassState.New:
                this.HandleNew(situation);
                break;
            default:
                throw new NotImplementedException();
                break;
        }
    }
    

    处理这种重复出现的设计问题的典型方法是 state pattern . 我必须说,它也可能变得相当麻烦,但拥有它至少会让您找到“一个”状态机,而不是到处重写代码,总的来说,我们努力提高可读性和可维护性。

    下面是一个围绕状态模式的典型包装器的示例(取自 this 实现,)这些是常用的,因为它们的语法消除了类中实现状态的复杂性:

    var phoneCall = new StateMachine<State, Trigger>(State.OffHook);
    
    phoneCall.Configure(State.OffHook)
        .Permit(Trigger.CallDialled, State.Ringing);
    
    phoneCall.Configure(State.Ringing)
        .Permit(Trigger.CallConnected, State.Connected);
    
    phoneCall.Configure(State.Connected)
        .OnEntry(() => StartCallTimer())
        .OnExit(() => StopCallTimer())
        .Permit(Trigger.LeftMessage, State.OffHook)
        .Permit(Trigger.PlacedOnHold, State.OnHold);
    
    // ...
    
    phoneCall.Fire(Trigger.CallDialled);
    Assert.AreEqual(State.Ringing, phoneCall.State);
    

    如你所见,有 states (例如:offhook),在这种状态下,通常通过一个事件(例如:pickedup)可以或不允许转换到另一个状态。当这种转换发生时,将执行一个操作。

        2
  •  1
  •   Sentinel    7 年前

    目前的问题模棱两可。

    如果需要状态模式,请对派生状态(如newstate)实现句柄。 对对象调用句柄,该对象调用state.handle()

    如果希望动态委派给方法重载,请实现handle(state1 s)、handle(state2s)等。通过将state强制转换为dynamic来调用它们,因此:handle((dynamic)state)