面向?qū)ο缶幊讨校愑脕肀硎緦?duì)象,一般情況下,我們需要考慮用類來表示什么具體的東西。類對(duì)應(yīng)的東西可能存在于真實(shí)世界中,也可能不存在于真實(shí)世界中。
狀態(tài)模式所表示的類,一般就不存在真實(shí)世界的某個(gè)東西,因?yàn)闋顟B(tài)模式中的類是用來表示狀態(tài)的。狀態(tài)一般都是抽象的,所以往往沒有具體對(duì)應(yīng)于真實(shí)世界的對(duì)象。
我們用類來表示狀態(tài),那么不同的狀態(tài)就用不同的類來表示,我們只要通過切換不同的類就可以切換不同的狀態(tài)。
狀態(tài)模式的具體實(shí)例
我們考慮設(shè)計(jì)一個(gè)金庫警報(bào)系統(tǒng),這個(gè)系統(tǒng)會(huì)根據(jù)白天晚上做出不同的響應(yīng)。
有一個(gè)金庫
金庫與警報(bào)中心相連
金庫里有警鈴和電話
金庫里有時(shí)鐘
金庫只能在白天使用
白天使用金庫,會(huì)在警報(bào)中心留下記錄
晚上使用金庫,會(huì)向警報(bào)中心發(fā)送緊急事態(tài)通知
警鈴白天晚上都能用
使用警鈴,會(huì)向警報(bào)中心發(fā)送緊急事態(tài)通知
電話都可以使用
白天使用電話,會(huì)呼叫警報(bào)中心
晚上使用電話,會(huì)呼叫警報(bào)中心的留言電話
基本就是以上的需求邏輯。
如果我們不使用狀態(tài)模式
那就是大概偽碼如下:
使用金庫調(diào)用的方法() {
if(白天) {
} else if(晚上) {
}
}
正常通話時(shí)() {
if(白天) {}
else if(晚上) {
}
}
顯然這樣可以實(shí)現(xiàn),也并沒有什么錯(cuò)誤。
但是狀態(tài)模式確實(shí)從不同的角度來考慮問題。
狀態(tài)模式會(huì)發(fā)現(xiàn),這些不同的行為,主要依賴于兩個(gè)狀態(tài),就是白天和晚上。所以狀態(tài)模式會(huì)抽象出這兩種狀態(tài),每個(gè)狀態(tài)就會(huì)有自己的行為實(shí)現(xiàn),比如白天這個(gè)狀態(tài)會(huì)實(shí)現(xiàn)自己的使用金庫的方法,通話的方法,晚上的類也會(huì)實(shí)現(xiàn)自己的行為邏輯,最后我們只要取得狀態(tài)對(duì)象的委托調(diào)用他們的方法就行了,不管他們具體是怎么實(shí)現(xiàn)的。
我們看一下使用狀態(tài)模式的偽碼:
白天的狀態(tài)類 {
使用金庫的方法
使用警鈴的方法
通話的方法
}
晚上的狀態(tài)類 {
使用金庫的方法
使用警鈴的方法
通話的方法
}
我們看到普通方法和狀態(tài)模式的區(qū)別就是狀態(tài)模式中,定義了狀態(tài)類,就不需要if語句來判斷了。所以當(dāng)我們遇到很多個(gè)ifelse語句的時(shí)候,往往可以考慮狀態(tài)模式,阿里最新的java開發(fā)手冊里面就有一條相關(guān)的推薦

下面我們就來具體實(shí)現(xiàn)代碼
定義狀態(tài)類的接口:
package State;
public interface State {
public abstract void doClock(Context context, int hour); // 設(shè)置時(shí)間
public abstract void doUse(Context context); // 使用金庫
public abstract void doAlarm(Context context); // 按下警鈴
public abstract void doPhone(Context context); // 正常通話
}
實(shí)現(xiàn)兩個(gè)具體的白天和晚上的狀態(tài)類
package State;
public class DayState implements State {
private static DayState singleton = new DayState();
private DayState() { // 構(gòu)造函數(shù)的可見性是private
}
public static State getInstance() { // 獲取唯一實(shí)例
return singleton;
}
public void doClock(Context context, int hour) { // 設(shè)置時(shí)間
if (hour < 9 || 17 <= hour) {
context.changeState(NightState.getInstance());
}
}
public void doUse(Context context) { // 使用金庫
context.recordLog("使用金庫(白天)");
}
public void doAlarm(Context context) { // 按下警鈴
context.callSecurityCenter("按下警鈴(白天)");
}
public void doPhone(Context context) { // 正常通話
context.callSecurityCenter("正常通話(白天)");
}
public String toString() { // 顯示表示類的文字
return "[白天]";
}
}
package State;
public class NightState implements State {
private static NightState singleton = new NightState();
private NightState() { // 構(gòu)造函數(shù)的可見性是private
}
public static State getInstance() { // 獲取唯一實(shí)例
return singleton;
}
public void doClock(Context context, int hour) { // 設(shè)置時(shí)間
if (9 <= hour && hour < 17) {
context.changeState(DayState.getInstance());
}
}
public void doUse(Context context) { // 使用金庫
context.callSecurityCenter("緊急:晚上使用金庫!");
}
public void doAlarm(Context context) { // 按下警鈴
context.callSecurityCenter("按下警鈴(晚上)");
}
public void doPhone(Context context) { // 正常通話
context.recordLog("晚上的通話錄音");
}
public String toString() { // 顯示表示類的文字
return "[晚上]";
}
}
Context類是負(fù)責(zé)管理狀態(tài)和聯(lián)系警報(bào)中心的接口,它定義了基本的警報(bào)中心的行為,
package State;
public interface Context {
public abstract void setClock(int hour); // 設(shè)置時(shí)間
public abstract void changeState(State state); // 改變狀態(tài)
public abstract void callSecurityCenter(String msg); // 聯(lián)系警報(bào)中心
public abstract void recordLog(String msg); // 在警報(bào)中心留下記錄
}
safeframe類實(shí)現(xiàn)了context接口
package State;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Color;
import java.awt.Button;
import java.awt.TextField;
import java.awt.TextArea;
import java.awt.Panel;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class SafeFrame extends Frame implements ActionListener, Context {
private TextField textClock = new TextField(60); // 顯示當(dāng)前時(shí)間
private TextArea textScreen = new TextArea(10, 60); // 顯示警報(bào)中心的記錄
private Button buttonUse = new Button("使用金庫"); // 金庫使用按鈕
private Button buttonAlarm = new Button("按下警鈴"); // 按下警鈴按鈕
private Button buttonPhone = new Button("正常通話"); // 正常通話按鈕
private Button buttonExit = new Button("結(jié)束"); // 結(jié)束按鈕
private State state = DayState.getInstance(); // 當(dāng)前的狀態(tài)
// 構(gòu)造函數(shù)
public SafeFrame(String title) {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
// 配置textClock
add(textClock, BorderLayout.NORTH);
textClock.setEditable(false);
// 配置textScreen
add(textScreen, BorderLayout.CENTER);
textScreen.setEditable(false);
// 為界面添加按鈕
Panel panel = new Panel();
panel.add(buttonUse);
panel.add(buttonAlarm);
panel.add(buttonPhone);
panel.add(buttonExit);
// 配置界面
add(panel, BorderLayout.SOUTH);
// 顯示
pack();
show();
// 設(shè)置監(jiān)聽器
buttonUse.addActionListener(this);
buttonAlarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
// 按鈕被按下后該方法會(huì)被調(diào)用
public void actionPerformed(ActionEvent e) {
System.out.println(e.toString());
if (e.getSource() == buttonUse) { // 金庫使用按鈕
state.doUse(this);
} else if (e.getSource() == buttonAlarm) { // 按下警鈴按鈕
state.doAlarm(this);
} else if (e.getSource() == buttonPhone) { // 正常通話按鈕
state.doPhone(this);
} else if (e.getSource() == buttonExit) { // 結(jié)束按鈕
System.exit(0);
} else {
System.out.println("?");
}
}
// 設(shè)置時(shí)間
public void setClock(int hour) {
String clockstring = "現(xiàn)在時(shí)間是";
if (hour < 10) {
clockstring += "0" + hour + ":00";
} else {
clockstring += hour + ":00";
}
System.out.println(clockstring);
textClock.setText(clockstring);
state.doClock(this, hour);
}
// 改變狀態(tài)
public void changeState(State state) {
System.out.println("從" + this.state + "狀態(tài)變?yōu)榱? + state + "狀態(tài)。");
this.state = state;
}
// 聯(lián)系警報(bào)中心
public void callSecurityCenter(String msg) {
textScreen.append("call! " + msg + "\n");
}
// 在警報(bào)中心留下記錄
public void recordLog(String msg) {
textScreen.append("record ... " + msg + "\n");
}
}
main類啟動(dòng)
package State;
public class Main {
public static void main(String[] args) {
SafeFrame frame = new SafeFrame("State Sample");
while (true) {
for (int hour = 0; hour < 24; hour++) {
frame.setClock(hour); // 設(shè)置時(shí)間
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
}

狀態(tài)模式的分析
狀態(tài)模式的角色:
state狀態(tài)
表示狀態(tài),定義了根據(jù)不同狀態(tài)進(jìn)行不同處理的接口,該接口是那些處理內(nèi)容依賴于狀態(tài)的方法集合,對(duì)應(yīng)實(shí)例的state類具體的狀態(tài)
實(shí)現(xiàn)了state接口,對(duì)應(yīng)daystate和nightstatecontext
context持有當(dāng)前狀態(tài)的具體狀態(tài)的實(shí)例,此外,他還定義了供外部調(diào)用者使用的狀態(tài)模式的接口。
狀態(tài)模式的類圖:
