|
Design Pattern - State
|
| (史帝芬, 2007/06/11, hi.steven@gmail.com) |
在許多程式裡,常可以看到為了判斷流程的狀態,使用一個很大的switch或一連串的if else, 那樣子寫程式也並非不可以,只是當流程很大,或流程隨需求改變而改變時,程式常會越改越亂, 在物件導向的程式設計裡,為這個情況提供的解決方案就是State Pattern。State Pattern透過將 每一個條件分支抽取成獨立的類別,以便將物件狀態也視為另一個獨立的物件,可獨立改變,如此, 在需求變更時,可減少程式的改變。 這裡將以一個請假流程為例,其流程如圖1所示,提出申請後由HR備查, HR會先檢查該員工的假是否還有剩,有的話才准假,如果假用完了,就退回給原申請者。 程式1是以switch直覺的寫成,可以發現,程式中以switch判斷在不同狀態下,應該做的事,當需求 改變,越來越大、越來越複雜時,可預見的,switch將會越來越大,其中的邏輯也會越來越複雜。 程式2則是以State Pattern改寫程式1,所有的條件判斷被獨立成一個個的類別,主程式LeaveFlow變 得很簡單,當然啦~這樣的寫法類別變得很多正是它的缺點。
using System;
namespace State
{
public class LeaveFlow
{
private const int START_STATE = 0;
private const int APPLY_STATE = 1;
private const int HR_STATE = 2;
private const int STOP_STATE = 3;
private int current_state;
private int totalCount = 7;
public LeaveFlow()
{
current_state = START_STATE;
}
public void nextState()
{
switch (current_state)
{
case START_STATE:
current_state = APPLY_STATE;
Console.WriteLine("提出申請");
break;
case APPLY_STATE:
current_state = HR_STATE;
Console.WriteLine("核准假單");
break;
case HR_STATE:
if (totalCount == 0)
{
current_state = APPLY_STATE;
Console.WriteLine("回申請流程");
}
else
{
totalCount--;
current_state = STOP_STATE;
Console.WriteLine("結束流程");
}
break;
case STOP_STATE:
Console.WriteLine("已結束流程");
break;
}
}
}
class Program
{
static void Main(string[] args)
{
LeaveFlow leave = new LeaveFlow();
leave.nextState();
leave.nextState();
leave.nextState();
Console.ReadLine();
}
}
}
using System;
namespace StatePattern
{
public class LeaveFlow
{
private State state;
private int totalCount = 7;
public int TotalCount
{
get { return totalCount; }
set { totalCount = value; }
}
public LeaveFlow()
{
state = new StartState();
}
public void nextState()
{
state.nextState(this);
}
public void setState(State state)
{
this.state = state;
}
}
public abstract class State
{
abstract public void nextState(LeaveFlow flow);
}
public class StartState : State
{
public override void nextState(LeaveFlow flow)
{
flow.setState(new ApplyState());
Console.WriteLine("提出申請");
}
}
public class ApplyState : State
{
public override void nextState(LeaveFlow flow)
{
flow.setState(new HRState());
Console.WriteLine("核准假單");
}
}
public class HRState : State
{
public override void nextState(LeaveFlow flow)
{
if (flow.TotalCount == 0)
{
flow.setState(new ApplyState());
Console.WriteLine("回申請流程");
}
else
{
flow.TotalCount = flow.TotalCount - 1;
flow.setState(new StopState());
Console.WriteLine("結束流程");
}
}
}
public class StopState : State
{
public override void nextState(LeaveFlow flow)
{
Console.WriteLine("流程已結束");
}
}
class Program
{
static void Main(string[] args)
{
LeaveFlow leave = new LeaveFlow();
leave.nextState();
leave.nextState();
leave.nextState();
Console.ReadLine();
}
}
}
![]() GoF書中的class diagram如圖所示,程式2使用state pattern避免了 一堆的if else或龐大的switch,許多人在看到HRState class中仍有if時,會認為這程式應該 有問題? 程式2使用state pattern省去了狀態轉移時的條件判斷,這裡的if判斷式判斷的是剩餘可 請的天數。 |