📢 本文由 gemini-3-flash-preview 翻譯
State Pattern 物件行為型模式
意圖
允許一個物件在其內部狀態改變時改變它的行為。物件看起來似乎修改了它的類別。
結構

其中:
Context (上下文) 定義用戶感興趣的介面:維護一個 ConcreteState 子類別的實例,這個實例定義當前狀態。
State (狀態) 定義一個介面以封裝與 Context 的一個特定狀態相關的行為。
ConcreteState (具體狀態子類別) 每個子類別實作與 Context 的一個狀態相關的行為。
適用性
範例 1
某大型商場內安裝了多台簡易的面紙販賣機,自動販售 2 元一包的面紙,且每次僅售出一包面紙。面紙販賣機的狀態圖如下圖所示:

採用狀態 (State) 模式來實作該面紙販賣機,得到如下圖所示的類別圖。其中類別 State 為抽象類別,定義了投幣、退幣、出面紙等方法介面。類別 SoldState、SoldOutState、NoQuarterState 和 HasQuarterState 分別對應上圖中面紙販賣機的 4 種狀態:售出面紙、面紙售完、尚未投幣、已投 2 元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| import java.util.*;
interface State {
public void insertQuarter(); // 投幣
public void ejectQuarter(); // 退幣
public void turnCrank(); // 按下「出面紙」按鈕
public void dispense(); // 出面紙
}
class TissueMachine {
State soldOutState, noQuarterState, hasQuarterState, soldState, state;
state = soldOutState;
int count = 0; // 面紙數量
public TissueMachine(int numbers) { /* 實作程式碼省略 */ }
public State getHasQuarterState() { return hasQuarterState; }
public State getNoQuarterState() { return noQuarterState; }
public State getSoldState() { return soldState; }
public State getSoldOutState() { return soldOutState; }
public int getCount() { return count; }
// 其餘程式碼省略
}
class NoQuarterState implements State {
TissueMachine tissueMachine;
public void insertQuarter() {
tissueMachine.setState(tissueMachine.getHasQuarterState());
}
// 建構方法以及其餘程式碼省略
}
class HasQuarterState implements State {
TissueMachine tissueMachine;
public void ejectQuarter() {
tissueMachine.setState(tissueMachine.getNoQuarterState());
}
// 建構方法以及其餘程式碼省略
}
class SoldState implements State {
TissueMachine tissueMachine;
public void dispense() {
if (tissueMachine.getCount() > 0) {
tissueMachine.setState(tissueMachine.getNoQuarterState());
} else {
tissueMachine.setState(tissueMachine.getSoldOutState()); }
}
}
|
範例 2
某航空公司的會員點數系統將其會員劃分為:普卡 (Basic)、銀卡 (Silver) 和金卡 (Gold) 三個等級。非會員 (NonMember) 可以申請成為普卡會員。會員的等級根據其一年內累積的哩程數進行調整。描述會員等級調整的狀態圖如下圖所示:

現採用狀態 (State) 模式實作上述場景,得到如下圖所示的類別圖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| import java.util.*;
abstract class CState {
public int flyMiles; // 哩程數
public abstract double travel(int miles, CFrequentFlyer context); // 根據累積哩程數調整會員等級
}
class CNoCustomer extends CState { // 非會員
public double travel(int miles, CFrequentFlyer context) {
System.out.println("Your travel will not account for points");
return miles; // 不累積哩程數
}
}
class CBasic extends CState { // 普卡會員
public double travel(int miles, CFrequentFlyer context) {
if (context.flyMiles >= 25000 && context.flyMiles < 50000)
context.setState(new CSilver());
if (context.flyMiles >= 50000)
context.setState(new CGold());
return miles;
}
}
class CGold extends CState { // 金卡會員
public double travel(int miles, CFrequentFlyer context) {
if (context.flyMiles >= 25000 && context.flyMiles < 50000)
context.setState(new CSilver());
if (context.flyMiles < 25000);
context.setState(new CBasic());
return miles + 0.5 * miles; // 累積哩程數
}
}
class CSilver extends CState { // 銀卡會員
public double travel(int miles, CFrequentFlyer context) {
if (context.flyMiles <= 25000)
context.setState(new CBasic());
if (context.flyMiles >= 50000)
context.setState(new CGold());
return (miles + 0.25 * miles); // 累積哩程數
}
}
class CFrequentFlyer {
CState state;
double flyMiles;
public CFrequentFlyer() {
state = new CNoCustomer();
flyMiles = 0;
setState(state);
}
public void setState(CState state) { this.state = state; }
public void travel(int miles) {
double bonusMiles = state.travel(miles, this);
flyMiles = flyMiles + bonusMiles;
}
}
|
範例 3
自動販賣機,有貨與無貨。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| public class StatePattern {
public static void main(String[] args) {
Context context = new Context();
context.Request(); // count = 2
context.Request(); // count = 1
context.Request(); // count = 0
context.Request(); // switch to State A
context.Request(); // count = 4
}
}
class Context{ // 販賣機
private int count;
private State state;
public Context(){
count = 3;
state = new StateA();
}
public int getCount() {
return count;
}
public State getState() {
return state;
}
public void setCount(int count) {
this.count = count;
}
public void setState(State state) {
this.state = state;
}
public void Request(){ // 購買飲料
state.Handle(this);
}
}
interface State{
public void Handle(Context context);
}
class StateA implements State{ // 有貨
@Override
public void Handle(Context context){
int count = context.getCount();
if(count >= 1){
context.setCount(count - 1);
System.out.println("Complete! あと" + context.getCount() + "個");
if(context.getCount() == 0){
context.setState(new StateB());
}
}else{
System.out.println("Refused!");
}
}
}
class StateB implements State{ // 無貨
@Override
public void Handle(Context context){
int count = context.getCount();
if(count == 0){
System.out.println("Refused!");
context.setCount(5);
System.out.println("Please try again");
context.setState(new StateA());
}else {
context.setState(new StateA());
context.Request();
}
}
}
|