1. 引入狀態模式
(該例子引入《大話設計模式》)
作爲程序員,加班是家常便飯。我們就每日的工作狀態寫一下。
9:00-11:00 上午上班
11:00-13:00 午休
13:00-18:00 下午上班
18:00 下班
18:00 以後 加班
用代碼表示如下:
public class WholeWork {
private int hour;
private boolean finished;
public void writeProgram() {
hour = this.getHour();
if(hour<11) {
System.out.println(hour+"點了,上午上班中");
} else if(hour<13) {
System.out.println(hour+"點了,午休中");
} else if(hour<18) {
System.out.println(hour+"點了,下午上班中");
} else{
if(finished) {
System.out.println(hour+"點了,下班了");
} else {
if(hour<21) {
System.out.println(hour+"點了,加班中");
}else {
System.out.println(hour+"點了,還在持續加班中,太困了,要睡着了");
}
}
}
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public boolean isFinished() {
return finished;
}
public void setFinished(boolean finished) {
this.finished = finished;
}
}
測試類:
public class WholeTest {
public static void main(String[] args) {
WholeWork work = new WholeWork();
work.setHour(9);
work.writeProgram();
work.setHour(12);
work.writeProgram();
work.setHour(16);
// work.setFinished(true);
work.writeProgram();
work.setHour(20);
work.writeProgram();
work.setHour(23);
work.writeProgram();
}
}
結果:
需求達到了,但從代碼可以看出這麼幾個問題:
- WholeWork類中的writeProgram方法過長,if判斷分支過多,責任過大,所有的邏輯判斷都寫在這個方法裏,違背了單一職責原則。
- 如果修改需求,比如說改成17:30下班,或者要求21點前必須下班,修改需求後,需要做的改動比較大,這就違背了開放-封閉原則。
爲了解決這種要求在不同狀態下需要發生不同的行爲,這就需要一種新的模式——狀態模式。
2. 狀態模式
2.1 定義
狀態模式,當一個對象的內在狀態改變時允許改變其行爲,這個對象看起來像是改變了其類。(引自《大話設計模式》)
2.2 解釋
其實,就是說當一個對象的狀態轉換的條件表達式過於複雜時,可以將狀態變化的判斷邏輯轉移到其他一系列類中去。就比如說上面的例子,9:00-11:00,11:00-13:00,等等,有這麼多的狀態條件判斷,我們可以把這些判斷分散到一系列類中,這樣不僅解了耦,代碼也變得簡單明瞭,方便維護。
當然了,這裏只適用狀態判斷複雜的情況,如果情況簡單,比如只需要兩個判斷,那麼if-else就可以搞定了,無需什麼狀態模式了。
2.3 結構圖
- Context類,上下文,定義狀態。
- State類,抽象狀態類,或接口類,定義了一個與Context狀態相關聯的行爲方法。
- ConcreteState類,具體實現類,實現State的方法,實現與Context狀態相關聯的行爲方法。
2.4 基本代碼
State
public interface State {
void handle(Context context);
}
Context上下文,定義了狀態
public class Context {
public State state;
public Context(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
ConcreteStateA,具體狀態類,每一個子類實現一個與Context相關的行爲。在handle方法中設置ConcreteStateA的下一狀態爲ConcreteStateB。
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("開始做A了");
context.state = new ConcreteStateB();
}
}
ConcreteStateB具體狀態類
public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("開始做B了");
}
}
測試類
public class StateTest {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA());//設置初始狀態爲ConcreteStateA
context.request();
context.request();
context.request();
}
}
結果:
3. 應用
就用狀態模式改編一下上面關於工作狀態的代碼。直接上代碼~
State類,抽象接口。
public interface State {
void writeProgram(Work work);
}
Work類,就是上面的Context。定義了hour和finished,即需要不斷變換的條件。維護了State接口。
public class Work {
public State state;
public int hour;
public boolean finished = false;
public Work(State state) {
this.state = state;
}
public void request() {
state.writeProgram(this);
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public boolean isFinished() {
return finished;
}
public void setFinished(boolean finished) {
this.finished = finished;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
}
具體實現類A
public class MorningState implements State {
@Override
public void writeProgram(Work work) {
int hour = work.getHour();
if(hour<11) {
System.out.println(hour+"點了,上午工作中");
}else {
work.setState(new NoonState());
work.request();
}
}
}
具體實現類B
public class NoonState implements State {
@Override
public void writeProgram(Work work) {
int hour = work.getHour();
if(hour<13) {
System.out.println(hour+"點了,中午休息中");
}else {
work.setState(new AfternooState());
work.request();
}
}
}
具體實現類C
public class AfternooState implements State {
@Override
public void writeProgram(Work work) {
int hour = work.getHour();
if(hour<18) {
System.out.println(hour+"點了,下午工作中");
}else {
work.setState(new EveningState());
work.request();
}
}
}
具體實現類D
public class EveningState implements State {
@Override
public void writeProgram(Work work) {
if(work.isFinished()) {
work.setState(new RestState());
work.request();
} else {
int hour = work.getHour();
if(hour<21) {
System.out.println(hour+"點了,夜晚加班中,累。。。");
}else {
work.setState(new SleepingState());
work.request();
}
}
}
}
具體實現類E
public class SleepingState implements State {
@Override
public void writeProgram(Work work) {
if(work.isFinished()) {
work.setState(new RestState());
work.request();
} else {
int hour = work.getHour();
System.out.println(hour+"點了,還在加班中,要睡着了。。。");
}
}
}
具體實現類F
public class RestState implements State {
@Override
public void writeProgram(Work work) {
System.out.println(work.getHour()+"點了,下班了。");
}
}
這裏,我們將不同狀態下所影響的行爲分別封裝在了子類ABCDEF中,實現具體的接口方法writeProgram。
測試類
public class StateTest {
public static void main(String[] args) {
Work work = new Work(new MorningState());//設置初始狀態爲上午
work.setHour(9);
work.request();
work.setHour(12);
work.request();
work.setHour(15);
work.request();
//work.setFinished(true);
work.setHour(20);
work.request();
work.setHour(23);
work.request();
}
}
結果:
如果將finished設置爲true,則結果爲:
完工~
4. 總結
- 消除了龐大的if-else判斷,降低了耦合性。
- 將不同狀態影響的行爲分散到子類中去,增強了擴展性,當有新的狀態行爲時,只需要增加子類就行了。
- 當一個對象的行爲取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行爲,這個時候可以考慮用狀態模式。
累。。今天先寫到這裏,我看看,之後有空再補充一個例子進來。
寫在最後:
本文主要是小貓看《大話設計模式》的筆記式的記錄,方便以後查閱。