避免处理函数使用 long switch/if

Avoiding long switch/if for handler functions

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

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 语句应该被分解出来。我考虑过一本字典,但它只是稍微移动了代码而没有真正改变架构。我怎样才能更好地处理这个问题?

当您想要扩展案例时,尤其是当您的同事没有上过逻辑设计课程时,必须考虑额外的复杂性:

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。我必须说,它也可能变得非常麻烦,但拥有它至少会引导您进入 'one' 状态机,而不是到处重写代码,一般来说,我们努力提高可读性和可维护性。

这是一个典型的状态模式包装器示例,(取自 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)转换到另一个状态是或不允许的.当发生这种转变时,将执行一个动作。

目前的问题是模棱两可的。

如果您想要状态模式,请在派生状态(例如 NewState)上实现句柄。 在对象上调用句柄,它调用 state.Handle()

如果你想对方法重载进行动态委托,实现 Handle(State1 s)、Handle(State2 s) 等。通过将状态转换为动态来调用它们,因此:Handle((dynamic)state)