State模式(狀態設計模式)
阿新 • • 發佈:2018-12-18
State???
State模式中,我們用類來表示狀態。以類來表示狀態後,我們就能通過切換類來方便地改變物件的狀態。當需要增加新的狀態時,如何修改程式碼這個問題也會很明確。
- 直接用狀態代替硬編碼 依賴於狀態的處理,來執行具體的操作
理清職責
- 實現功能:
·有一個金庫 ·金庫與警報中心相連 ·金庫裡有警鈴和正常通話用的電話·金庫裡有時鐘,監視著現在的時間 ·白天的時間範圍是9:00~16:59,晚上的時間範圍是17:00~23:59和0:00~8:59 ·金庫只能在白天使用 ·白天使用金庫的話,會在警報中心留下記錄 ·晚上使用金庫的話,會向警報中心傳送緊急事態通知 ·任何時候都可以使用警鈴 ·使用警鈴的話,會向警報中心傳送緊急事態通知 ·任何時候都可以使用電話(但晚上只有留言電話) ·白天使用電話的話,會呼叫警報中心 ·晚上用電話的話,會呼叫警報中心的留言電話
名字=======》》》》》說明 State ||表示金庫狀態的介面 DayState ||表示“白天”狀態的類。它實現了State介面 NightState ||表示“晚上”狀態的類。它實現了State介面 Context ||表示管理金庫狀態,並與警報中心聯絡的介面 SafeFrame ||實現了Context介面。在它內部持有按鈕和畫面顯示等UI資訊 Main || 測試程式行為的類
- 使用與不使用狀態模式對比
- 不使用
使用金庫時被呼叫的方法(){ if(白天){ 向警報中心報告使用記錄 ]elseif(晚上){ 向警報中心報告緊急事態 警鈴響起時被呼叫的方法(){ 向警報中心報告緊急事態 正常通話時被呼叫的方法(){ if(白天){ 呼叫警報中心 }elseif(晚上){ 呼叫警報中心的留言電話 }
- 使用
表示百天的狀態的類{ 使用金庫時被呼叫的方法(){ 向警報中心報告使用記錄 警鈴響起時被呼叫的方法(){ 向警報中心報告緊急事態 正常通話時被呼叫的方法(){ 呼叫警報中心 表示晚上的狀態的類{ 使用金庫時被呼叫的方法(){ 向警報中心報告緊急事態 警鈴響起時被呼叫的方法(){ 向警報中心報告緊急事態 正常通話時被呼叫的方法(){ 呼叫警報中心的留言電話 - 相關設計模式 ◆Singleton模式(第5章)Singleton 模式常常會出現在ConcreteState角色中。在示例程式中,我們就使用了Singleton模式。這是因為在表示狀態的類中並沒有定義任何例項欄位(即表示例項的狀態的欄位)。 ◆Flyweight模式(第20章)在表示狀態的類中並沒有定義任何例項欄位。因此,有時我們可以使用Flyweight模式在多個Context 角色之間共享ConcreteState角色。
UML
時序圖:
Code
- DayState \NightState State
public interface State {
//設定時間
void doclock(Context context, int hour);
// 使用金庫
void doUse(Context context);
// 按下警鈴
void doAlarm(Context context);
// 正常通話
void dophone(Context context);
}
public class NightState implements State {
private NightState() {
}
private static NightState singleton = new NightState();
public static State getInstance() {
return (State) singleton;
}
@Override
public void doclock(Context context, int hour) {
if (hour >= 9 && hour < 17) {
context.changeState(DayState.getInstance());
}
}
@Override
public void doUse(Context context) {
context.recordLog("使用金庫[晚上]");
}
@Override
public void doAlarm(Context context) {
context.callSecurityCenter("按下警鈴[晚上]");
}
@Override
public void dophone(Context context) {
context.recordLog("正常通話[晚上]");
}
@Override
public String toString() {
return "DayState{晚上}";
}
}
public class DayState implements State {
/**
* 這裡使用單例模式,因為每次改變一次狀態都會生成一次例項,非常浪費記憶體與時間
*/
private DayState() {
}
private static DayState singleton = new DayState();
public static State getInstance() {
return singleton;
}
@Override
public void doclock(Context context, int hour) {
if (hour < 9 || hour >= 17) {
context.changeState(NightState.getInstance());
}
}
@Override
public void doUse(Context context) {
context.recordLog("使用金庫[白天]");
}
@Override
public void doAlarm(Context context) {
context.callSecurityCenter("按下警鈴[白天]");
}
@Override
public void dophone(Context context) {
context.recordLog("正常通話[白天]");
}
@Override
public String toString() {
return "DayState{白天}";
}
}
- Context 、SateFrame 、MainT
···
public class MainT {
public static void main(String[] args) {
SateFrame frame = new SateFrame("Safe Smaple");
// 24個小時制
while (true){
for (int i = 0; i < 24; i++) {
frame.setClock(i);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
public interface Context { //設定時間 void setClock(int hour);
// 改變狀態
void changeState(State state);
// 聯絡警報中心
void callSecurityCenter(String msg);
// 在警報中心留下記錄
void recordLog(String msg);
} public class SateFrame extends Frame implements ActionListener,Context {
// 顯示時間
private TextField textClock=new TextField(60);
// 顯示警報中心的記錄
private TextArea textScreen=new TextArea(10,60);
private Button buttonUse=new Button("使用金庫");
private Button buttonALarm=new Button("按下警鈴");
private Button buttonPhone=new Button("正常通話");
private Button buttonExit=new Button("退出");
// 初始狀態為白天
private State state=DayState.getInstance();
public SateFrame(String title) throws HeadlessException {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
add(textClock,BorderLayout.NORTH);
textClock.setEditable(false);
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();
buttonUse.addActionListener(this);
buttonALarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
/**
* 可以看出這裡的操作就簡化很多了:
* 基本只有業務邏輯程式碼:
* 判斷狀態相關的程式碼可以直接由相關的狀態程式碼實現,
* 即為由類的狀態代替了if else程式碼
*/
@Override
public void actionPerformed(ActionEvent e) {
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){
System.exit(0);
}else{
System.out.println("?");
}
}
@Override
public void setClock(int hour) {
String clockstring="現在時間是:";
if(hour<10){
clockstring+="0"+hour+":00";
}else{
clockstring+=hour+":00";
}
System.out.println(clockstring);
textClock.setText(clockstring);
state.doclock(this,hour);
}
@Override
public void changeState(State state) {
System.out.println("從"+this.state+"狀態變為了"+state+"狀態。");
this.state=state;
}
@Override
public void callSecurityCenter(String msg) {
textScreen.append("呼叫---"+msg+"\n");
}
@Override
public void recordLog(String msg) {
textScreen.append("記錄---"+msg+"\n");
}
}
···