介紹
狀態(tài)模式和策略模式是一對雙胞胎艳汽,他們都屬于行為設(shè)計模式领曼。狀態(tài)模式和策略模式都是為具有多種可能情形設(shè)計的模式布持,把不同的處理情形抽象為一個相同的接口题暖,符合對擴展開放捉超,對修改封閉的原則。策略模式封裝了一組相關(guān)算法拼岳,它允許Client在運行時使用可互換的行為;狀態(tài)模式幫助一個類在不同的狀態(tài)顯示不同的行為侧啼。狀態(tài)模式封裝了對象的狀態(tài)痊乾,而策略模式封裝算法或策略哪审。因為狀態(tài)是跟對象密切相關(guān)的虑瀑,它不能被重用;而通過從Context中分離出策略或算法叽奥,我們可以重用它們朝氓。
狀態(tài)模式(State Pattern) 允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為,對象看起來似乎修改了它的類待德。對象的行為依賴于它的狀態(tài)(屬性)将宪,并且可以根據(jù)它的狀態(tài)改變而改變它的相關(guān)行為橡庞。
TCP協(xié)議中的三次握手和四次斷開也可以使用狀態(tài)模式來實現(xiàn)扒最。
結(jié)構(gòu)圖
時序圖
案例
我就用工作日的上班時間來舉例子
6種狀態(tài)
- 23點到早上7點
- 7點到9點
- 9點到12點
- 12點到13點
- 13點到18點
- 18點到23點
State 抽象類
public abstract class State {
public abstract void handle(WorkDay workDay);
public void changeState(WorkDay workDay){
State state = null;
if (23 < workDay.getHour() || workDay.getHour() <= 7) {
state = new SleepState();
}
if (7 < workDay.getHour() && workDay.getHour() <= 9) {
state = new EarlyMorningState();
}
if (9 < workDay.getHour() && workDay.getHour() <= 12) {
state = new MorningState();
}
if (12 < workDay.getHour() && workDay.getHour() <= 13) {
state = new MiddayState();
}
if (13 < workDay.getHour() && workDay.getHour() < 18) {
state = new AfternoonState();
}
if (18 < workDay.getHour() && workDay.getHour() <= 23) {
state = new NightState();
}
workDay.setState(state);
workDay.doWork();
}
}
23點到早上7點睡覺狀態(tài)SleepState
public class SleepState extends State {
@Override
public void handle(WorkDay workDay) {
if (23 < workDay.getHour() || workDay.getHour() <= 7) {
System.out.println("現(xiàn)在是" + workDay.getHour() + "點确封,晚上睡覺時間!");
} else {
changeState(workDay);
}
}
}
**7點到9點清晨狀態(tài)EarlyMorningState **
public class EarlyMorningState extends State {
@Override
public void handle(WorkDay workDay) {
if (7 < workDay.getHour() && workDay.getHour() <= 9) {
System.out.println("現(xiàn)在是" + workDay.getHour() + "點爪喘,清晨秉剑,準備上班!");
} else {
changeState(workDay);
}
}
}
9點到12點上午上班狀態(tài)MorningState
public class MorningState extends State{
@Override
public void handle(WorkDay workDay) {
if (9 < workDay.getHour() && workDay.getHour() <= 12) {
System.out.println("現(xiàn)在是" + workDay.getHour() + "點侦鹏,上午工作時間!");
} else {
changeState(workDay);
}
}
}
12點到13點中午休息狀態(tài)MiddayState
public class MiddayState extends State {
@Override
public void handle(WorkDay workDay) {
if (12 < workDay.getHour() && workDay.getHour() <= 13) {
System.out.println("現(xiàn)在是"+workDay.getHour()+"點略水,中午休息時間!");
} else {
workDay.setState(new NightState());
workDay.doWork();
}
}
}
13點到18點下午工作狀態(tài)AfternoonState
public class AfternoonState extends State {
@Override
public void handle(WorkDay workDay) {
if (13 < workDay.getHour() && workDay.getHour() <= 18) {
System.out.println("現(xiàn)在是" + workDay.getHour() + "點渊涝,下午工作時間!");
} else {
changeState(workDay);
}
}
}
18點到23點晚上下班狀態(tài)NightState
public class NightState extends State {
@Override
public void handle(WorkDay workDay) {
if (18 < workDay.getHour() && workDay.getHour() <= 23) {
System.out.println("現(xiàn)在是" + workDay.getHour() + "點床嫌,晚上下班時間!");
} else {
changeState(workDay);
}
}
}
工作日類WorkDay
public class WorkDay {
private State state = new NightState();
private int hour;
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public void doWork(){
state.handle(this);
}
}
調(diào)用類Client
public class Client {
public static void main(String[] args) {
WorkDay workDay = new WorkDay();
for (int i = 0; i < 55; i++) {
workDay.setHour(i%24);
workDay.doWork();
}
}
}
運行結(jié)果
總結(jié)
在很多情況下鳖谈,一個對象的行為取決于一個或多個動態(tài)變化的屬性缆娃,這樣的屬性叫做狀態(tài),這樣的對象叫做有狀態(tài)的(stateful)對象龄恋,這樣的對象狀態(tài)是從事先定義好的一系列值中取出的凶伙。當一個這樣的對象與外部事件產(chǎn)生互動時,其內(nèi)部狀態(tài)就會改變显押,從而使得系統(tǒng)的行為也隨之發(fā)生變化乘碑。
環(huán)境類實際上就是擁有狀態(tài)的對象金拒,環(huán)境類有時候可以充當狀態(tài)管理器(State Manager)的角色,可以在環(huán)境類中對狀態(tài)進行切換操作资铡。
抽象狀態(tài)類可以是抽象類笤休,也可以是接口症副,不同狀態(tài)類就是繼承這個父類的不同子類,狀態(tài)類的產(chǎn)生是由于環(huán)境類存在多個狀態(tài)闹啦,同時還滿足兩個條件: 這些狀態(tài)經(jīng)常需要切換辕坝,在不同的狀態(tài)下對象的行為不同圣勒。因此可以將不同對象下的行為單獨提取出來封裝在具體的狀態(tài)類中,使得環(huán)境類對象在其內(nèi)部狀態(tài)改變時可以改變它的行為挚歧,對象看起來似乎修改了它的類吁峻,而實際上是由于切換到不同的具體狀態(tài)類實現(xiàn)的在张。由于環(huán)境類可以設(shè)置為任一具體狀態(tài)類帮匾,因此它針對抽象狀態(tài)類進行編程瘟斜,在程序運行時可以將任一具體狀態(tài)類的對象設(shè)置到環(huán)境類中,從而使得環(huán)境類可以改變內(nèi)部狀態(tài)痪寻,并且改變行為橡类。