#狀態模式(State Pattern)
##簡介
狀態模式,當一個對象在內在狀態改變時,允許改變起行為,這個對象看起來像是改變了其類。
狀態模式主要解決的是當控制一個對象狀態轉換的條件表達式過于復雜的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把復雜的判斷邏輯簡化。
##簡例
###工作狀態-分類板
```
public class Work
{
private int hour;
public int Hour
{
get {return hour;}
set {hour = value;}
}
private bool finish = false;
public bool TaskFinished
{
get {return finish;}
set { finish = value;}
}
public void WriteProgram()
{
if (hour < 12)
{
Console.WriteLine("當前時間:{0}點,上午",hour);
} else if (hour < 13)
{
Console.WriteLine("當前時間:{0}點,午飯",hour);
} else if (hour < 17)
{
Console.WriteLine("當前時間:{0}點,下午",hour);
} else {
if (finish) {
Console.WriteLine("當前時間:{0}點,下班回家",hour);
} else {
if (hour < 21)
{
Console.WriteLine("當前時間:{0}點,加班哦,疲累之極",hour);
} else {
Console.WriteLine("當前時間:{0}點,不行了,睡著了",hour);
}
}
}
}
}
```
客戶端程序如下
```
static void Main(string[[] args)
{
//緊急項目
Work emergencyProjects = new Work();
emergencyProjects.Hour = 9;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 10;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 12;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 13;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 14;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 17;
emergencyProjects.WriteProgram();
//emergencyProjects.WorkFinished = true;
emergencyProjects.TaskFinished = false;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 19;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 22;
emergencyProjects.WriteProgram();
Console.Read();
}
```
###結果
此段代碼有很大的問題。MartinFowler曾在《重構》中寫過一個很重要的代碼味道,叫做'Long Method',方法如果過長其實極有可能有壞味道了。
'Work'類的'WriteProgram'方法很長,而且有很多判斷的分支,這也就意味著它的責任過大了。無論是任何狀態,都需要通過它來改變,這實際上是很糟糕的。
***面對對象設計其實就是希望做到代碼的責任分解。***這個類違背了'單一職責原則'。而且由于'WriteProgram'的方法里面有這么多判斷,使得任何需求的改動或增加,都需要更改這個方法。又違背了'開放-封閉原則'。這類有個解決方案,就是'狀態模式'。
***狀態模式的好處是將與特定狀態相關的行為局部化,并且將不同狀態的行為分割開來。***
***將特定的狀態相關的行為都放入一個對象中,由于所有與狀態相關的代碼都存在于摸個ConcreteState中,所以通過定義新的字類可以很容易地增加新的狀態與轉換。這樣做的目的就是為了消除龐大的條件分支語句。狀態模式通過把各種狀態轉移邏輯分布到State的子類之間,來減少相互間的依賴。***
***當一個對象的行為取決于它的狀態,并且它必須在運行時刻根據狀態改變它的行為時,就可以考慮使用狀態模式了。***
###工作狀態-狀態模式板
```
//抽象狀態
public abstract class State
{
public abstract void WriteProgram(Work w);
}
```
上午工作狀態
```
public class ForenoonState: State
{
public override void WriteProgram (Work w)
{
if (w.hour < 12)
{
Console.WriteLine("當前時間:{0}點 上午",w.Hour);
} else
{
w.SetState(new NoonState());
w.WriteProgram();
}
}
}
```
中午工作狀態
```
public class NoonState: State
{
public override void WriteProgram (Work w)
{
if (w.hour < 13)
{
Console.WriteLine("當前時間:{0}點 中午",w.Hour);
} else
{
w.SetState(new AfterNoonState());
w.WriteProgram();
}
}
}
```
下午工作狀態
```
public class AfterNoonState: State
{
public override void WriteProgram (Work w)
{
if (w.hour < 13)
{
Console.WriteLine("當前時間:{0}點 下午",w.Hour);
} else
{
w.SetState(new EveningState());
w.WriteProgram();
}
}
}
```
晚間工作狀態
```
public class EveningState: State
{
public override void WriteProgram (Work w)
{
if (w.TaskFinished)
{
w.SetState(new RestState());
w.WriteProgeam();
} else
{
if (w.Hour < 21)
{
Console.WriteLine("當前時間:{0}點 加班哦,疲累",w.Hour);
} else
{
w.SetState(new SleepState());
w.WriteProgram();
}
}
}
}
```
睡眠狀態
```
public class SleepingState: State
{
public override void WriteProgram (Work w)
{
if (w.hour < 13)
{
Console.WriteLine("當前時間:{0}點 不行了,睡著了。",w.Hour);
}
}
}
```
下班休息狀態
```
public class RestState: State
{
public override void WriteProgram(Work w)
{
Console.WriteLine("當前時間:{0}點 下班回家了。",w.Hour);
}
}
```
工作類,此時沒有了過長的分支判斷語句
```
public class Work
{
private State current;
public Work()
{
current = new ForenoonState();
}
private double hour;
public double Hour
{
get {return hour;}
set {hour = value;}
}
private bool finish = false;
public bool TaskFinshed
{
get { return finsh;}
set { finished = value;}
}
public void SetState(State s)
{
current = s;
}
public void WriteProgram()
{
current.WriteProgram(this);
}
}
```
客戶端代碼,沒有任何改動。但我們的程序卻更加靈活易變了。
```
static void Main(string[[] args)
{
//緊急項目
Work emergencyProjects = new Work();
emergencyProjects.Hour = 9;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 10;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 12;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 13;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 14;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 17;
emergencyProjects.WriteProgram();
//emergencyProjects.WorkFinished = true;
emergencyProjects.TaskFinished = false;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 19;
emergencyProjects.WriteProgram();
emergencyProjects.Hour = 22;
emergencyProjects.WriteProgram();
Console.Read();
}
```
##總結與分析
狀態模式的主要優點在于封裝了轉換規則,并枚舉可能的狀態,它將所有與某個狀態有關的行為放到一個類中,并且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為,還可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數;其缺點在于使用狀態模式會增加系統類和對象的個數,且狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂,對于可以切換狀態的狀態模式不滿足“開閉原則”的要求。