Java設計模式之狀態模式(State)

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判斷,降低了耦合性。
  • 將不同狀態影響的行爲分散到子類中去,增強了擴展性,當有新的狀態行爲時,只需要增加子類就行了。
  • 當一個對象的行爲取決於它的狀態,並且它必須在運行時刻根據狀態改變它的行爲,這個時候可以考慮用狀態模式。

 

 

累。。今天先寫到這裏,我看看,之後有空再補充一個例子進來。

 

寫在最後:

本文主要是小貓看《大話設計模式》的筆記式的記錄,方便以後查閱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章