從2006年-2012年,在全國計算機技術與軟件專業技術資格(水平)考試軟件設計師級別考試中,狀態模式已兩次作為下午試題的最後一題出現(參考:歷年軟件設計師下午考試試題匯總統計),分別是2006年下半年和2011年下半年的兩次考試。
【全國計算機技術與軟件專業技術資格(水平)考試 2006 年下半年 軟件設計師 下午試卷】
註:當年試題六和試題七二選一,試題六為C++版,試題七為Java版。
試題六
閱讀以下說明和 C++代碼,將應填入 (n) 處的字句寫在答題紙的對應欄內。
【說明】
傳輸門是傳輸系統中的重要裝置。傳輸門具有 Open(打開)、Closed(關閉)、Opening(正在打開)、StayOpen(保持打開)、Closing(正在關閉)五種狀態。觸發傳輸門狀態轉 換的事件有 click、complete 和 timeout 三種。事件與其相應的狀態轉換如圖6-1 所示。
圖6-1 傳輸門響應事件與其狀態轉換圖
下面的【C++代碼 1】與【C++代碼 2】分別用兩種不同的設計思路對傳輸門進行狀態模擬,請填補代碼中的空缺。
【C++代碼 1】
const int CLOSED = 1; const int OPENING = 2;
const int OPEN = 3; const int CLOSING = 4;
const int STAYOPEN = 5; //定義狀態變量,用不同整數表示不同狀態
class Door {
private:
int state; //傳輸門當前狀態
void setState(int state){ this->state = state; } //設置當前狀態
public: Door():state(CLOSED){};
void getState() { //根據當前狀態輸出相應的字符串
switch(state) {
case OPENING: cout <<"OPENING" << endl; break;
case CLOSED: cout << "CLOSED" << endl; break;
case OPEN: cout << "OPEN" << endl; break;
case CLOSING: cout << "CLOSING" << endl; break;
case STAYOPEN: cout << "STAYOPEN" << endl; break;
}
}
void click() { //發生click事件時進行狀態轉換
if ((1) ) setState(OPENING);
else if ((2) ) setState(CLOSING);
else if ((3) ) setState(STAYOPEN);
}
void timeout(){ //發生timeout事件時進行狀態轉換
if (state == OPEN) setState(CLOSING);
}
void complete(){ //發生complete事件時進行狀態轉換
if (state == OPENING) setState(OPEN);
else if (state == CLOSING) setState(CLOSED);
}
};
int main() {
Door aDoor;
aDoor.getState();
aDoor.click();
aDoor.getState();
aDoor.complete();
aDoor.getState();
aDoor.click();
aDoor.getState();
aDoor.click();
aDoor.getState();
return 0;
}
【C++代碼 2】
class Door {
public:
DoorState *CLOSED, *OPENING, *OPEN, *CLOSING, *STAYOPEN, *state;
Door();
virtual ~Door(){ …… //釋放申請的內存,此處代碼省略};
void setState(DoorState *state) { this->state = state; }
void getState(){
// 此處代碼省略,本方法輸出狀態字符串,
// 例如,當前狀態為CLOSED時,輸出字符串為“CLOSED”
}
void click();
void timeout();
void complete();
};
Door::Door() {
CLOSED = new DoorClosed(this);
OPENING = new DoorOpening(this);
OPEN = new DoorOpen(this);
CLOSING = new DoorClosing(this);
STAYOPEN = new DoorStayOpen(this);
state = CLOSED;
}
void Door::click() { (4) ;}
void Door::timeout() { (5) ; }
void Door::complete() {(6) ; }
class DoorState { //定義一個抽象的狀態,它是所有狀態類的基類
protected: Door *door;
public:
DoorState(Door *door) { this->door = door; }
virtual ~DoorState(void);
virtual void click() {}
virtual void complete() {}
virtual void timeout() {}
};
class DoorClosed :public DoorState{ //定義一個基本的 Closed 狀態
public:
DoorClosed(Door *door):DoorState(door) {}
virtual ~ DoorClosed (){}
void click();
};
void DoorClosed::click() {(7) ; }
// 其它狀態類的定義與實現代碼省略
int main() {
Door aDoor;
aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.complete();
aDoor.getState(); aDoor.timeout(); aDoor.getState(); return 0;
}
試題七
閱讀以下說明以及Java程序,將應填入 (n) 處的字句寫在答題紙的對應欄內。
【說明】
傳輸門是傳輸系統中的重要裝置。傳輸門具有 Open(打開)、Closed(關閉)、Opening(正在打開)、StayOpen(保持打開)、Closing(正在關閉)五種狀態。觸發狀態的轉換事件有 click、complete 和 timeout 三種。事件與其相應的狀態轉換如圖7-1所示。
圖7-1 傳輸門響應事件與其狀態轉換圖
下面的【Java 代碼 1】與【Java 代碼 2】分別用兩種不同的設計思路對傳輸門進行狀態模擬,請填補代碼中的空缺。
【Java代碼 1】
public class Door {
//定義狀態變量,用不同的整數表示不同狀態
public static final int CLOSED = 1;
public static final int OPENING = 2;
public static final int OPEN = 3;
public static final int CLOSING = 4;
public static final int STAYOPEN = 5;
private int state = CLOSED;
private void setState(int state){this.state = state;} //設置傳輸門當前狀態
public void getState() {
// 此處代碼省略,本方法輸出狀態字符串,
// 例如,當前狀態為 CLOSED 時,輸出字符串為”CLOSED”
}
public void click() { //發生 click 事件時進行狀態轉換
if ((1) ) setState(OPENING);
else if ( (2) ) setState(CLOSING);
else if ((3) ) setState(STAYOPEN);
}
//發生 timeout 事件時進行狀態轉換
public void timeout() { if (state == OPEN) setState(CLOSING); }
public void complete() { //發生 complete 事件時進行狀態轉換
if (state == OPENING) setState(OPEN);
else if (state == CLOSING) setState(CLOSED);
}
public static void main(String [] args){
Door aDoor = new Door();
aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.complete(); aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.click(); aDoor.getState(); return;
}
}
【Java代碼 2】
public class Door {
public final DoorState CLOSED = new DoorClosed(this);
public final DoorState OPENING = new DoorOpening(this);
public final DoorState OPEN = new DoorOpen(this);
public final DoorState CLOSING = new DoorClosing(this);
public final DoorState STAYOPEN = new DoorStayOpen(this);
private DoorState state = CLOSED;
//設置傳輸門當前狀態
public void setState(DoorState state){ this.state = state;}
public void getState(){ //根據當前狀態輸出對應的狀態字符串
System.out.println(state.getClass().getName());
}
public void click() { (4) ;}//發生 click 事件時進行狀態轉換
public void timeout() { (5) ;}//發生 timeout 事件時進行狀態轉換
public void complete() { (6) ;}//發生 complete 事件時進行狀態轉換
public static void main(String[] args){
Door aDoor = new Door();
aDoor.getState(); aDoor.click(); aDoor.getState(); aDoor.complete();
aDoor.getState(); aDoor.timeout(); aDoor.getState(); return;
}
}
public abstract class DoorState { //定義所有狀態類的基類
protected Door door ;
public DoorState(Door door) {this.door = door;}
public void click() {} public void complete() {} public void timeout() {}
}
class DoorClosed extends DoorState{ //定義一個基本的 Closed 狀態
public DoorClosed(Door door) { super(door); }
public void click() { (7) ; }
//該類定義的其餘代碼省略
}
//其餘代碼省略
——————————————————————————————————————————————————
解答:
本題前三空的難度不大,隻要能夠看懂狀態圖並讀懂代碼,正確填寫應該沒有太大問題,後四空略有難度,特別是最後一空。在本試題中提供瞭兩種狀態轉換實現方案:
【方案一】(即代碼一)沒有用狀態模式,將所有的狀態轉換代碼都寫到Door類中,每一個狀態對應一個整型常量(狀態變量),當Door的click()等方法被調用時,可以實現相應狀態的轉換,根據狀態圖,可以完成對(1)、(2)和(3)空的解答,例如當狀態變量state為CLOSED或者CLOSING時,調用click()方法,狀態將轉換為OPENING。同理,當timeout()方法和complete()方法被調用時,某些狀態之間也可以發生轉換。
【方案二】(即代碼二)使用瞭狀態模式,Door類充當環境類,提供瞭抽象類DoorState充當抽象狀態類,其子類,如DoorClosed、DoorOpening等,充當具體狀態類。在環境類Door枚舉瞭所有的狀態,提供瞭一個setState()方法用於設置當前狀態,此外,在Door的click()方法中調用狀態類的click()方法,在Door的timeout()方法中調用狀態類的timeout()方法,在Door的complete()方法中調用狀態類的complete()方法。因此,(4)、(5)、(6)空用於實現間接調用。第(7)空用於實現狀態的切換,當狀態為DoorClosed時,如果調用click()方法,那麼Door的當前狀態將切換到OPENING,因此在第(7)空,將調用door對象的setState()方法,註入一個DoorOpening類型的對象,由於該對象已存在於Door中,無須再重新創建。在C++中,可以通過door->OPENING來獲取,而在Java中則通過door.OPENING來獲取。在本試題中,狀態的切換由具體狀態類來完成,由環境類來維護各個具體狀態類的實例。