State Pattern 对象行为型模式
意图
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类
结构
其中:
-
Context (上下文) 定义客户感兴趣的接口:维护一个 ConcerteState 子类的实例,这个实例定义当前状态
-
State (状态) 定义一个接口以封装与 Context 的一个特定状态相关的行为
-
ConcreteState (具体状态子类) 毎个子类实现与 Context 的一个状态相关的行为
适用性
-
一个对象的行为决定于它的状态,并且它必领在运行吋刻根据状态改变它的行为
-
一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构,State 模式将毎一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化
例子 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();
}
}
}