Home 状态模式
Post
Cancel

状态模式

State Pattern 对象行为型模式

意图

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类

结构

状态模式

其中:

  • Context (上下文) 定义客户感兴趣的接口:维护一个 ConcerteState 子类的实例,这个实例定义当前状态

  • State (状态) 定义一个接口以封装与 Context 的一个特定状态相关的行为

  • ConcreteState (具体状态子类) 毎个子类实现与 Context 的一个状态相关的行为

适用性

  • 一个对象的行为决定于它的状态,并且它必领在运行吋刻根据状态改变它的行为

  • 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构,State 模式将毎一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化

例子 1

某大型商场内安装了多个简易的纸巾售卖机,自动出售2元钱一包的纸巾,且每次仅售出一包纸巾。纸巾售卖机的状态图如下图所示

状态模式-例1-图1

采用状态 (State) 模式来实现该纸巾售卖机,得到如下图所示的类图。其中类 State 为抽象类,定义了投币、退币、出纸巾等方法接口。类 SoldState、SoldOutState、NoQuarterState 和 HasQuarterState 分别对应上图中纸巾售卖机的4种状态:售出纸巾、纸巾售完、没有投币、有2元钱

状态模式-例1-图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) 可以申请成为普卡会员。会员的等级根据其一年内累积的里程数进行调整。描述会员等级调整的状态图如下图所示

状态模式-例2-图1

现采用状态 (State) 模式实现上述场景,得到如下图所示的类图

状态模式-例2-图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
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();
        }
    }
}
This post is licensed under CC BY 4.0 by the author.

Ajax 与 Axios

策略模式