避免处理函数使用 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)
我发现我经常写这样的代码:
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)