状态模式(State Pattern)

定义

当一个对象的内在状态改变时,允许改变其行为。

使用场景

  • 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。

  • 代码中包含大量与对象状态有关的if-else或switch-case等条件语句。

优缺点

优点:

  • 将所有与一个特定状态相关的行为都放入一个状态类中,将烦琐的状态判断转换成结构清晰的状态类族,提升了可扩展性和可维护性。

缺点:

  • 状态模式的使用必然会增加类和对象的个数。

  • 随着状态的增加,子类会变得繁多。

UML类图

状态模式

  • Context:用来操作一个State子类的上下文环境。

  • State:状态的抽象基类或接口。定义了一组接口,表示对象在各状态下的行为。

  • ConcreteStateA、ConcreteStateB:具体的状态类。实现某一具体状态下,该对象的特定行为。从而达到不同状态下的不同行为。

与策略模式的区别

  • 从UML类图来看,状态模式与策略模式的结构几乎完全一样

  • 它们的目的、本质却完全不一样。状态模式是解决不同状态下有不同的对象行为的问题;而策略模式是解决同一性质问题的一系列算法

  • 状态模式的行为是平行的、不可替换的,而策略模式的行为是彼此独立的、可相互替换的

状态模式的常见实现

状态模式的常见实现方式有:

  • 一个类中包含所有的行为,每个行为由一个方法负责,每个方法判断当前状态值作不同的处理。

  • 规范的状态模式,一个状态抽象基类,每个状态实现类包含该状态下的对象行为。

第1种方式被称为硬编码方式,其违反了OCP、单一职责等设计模式原则,而且会越来越臃肿,导致维护成本很高。而第2种方式符合设计模式思想,其可扩展性、可维护性很高。

示例:利用第1种方式实现的“在不同状态下,电视机的不同行为”。

public class TvController {
    
    public static final int POWER_ON = 1;
    public static final int POWER_OFF = 2;
    
    private int mState = POWER_OFF;
    
    public void powerOn() {
        mState = POWER_ON;
    }
    
    public void powerOff() {
        mState = POWER_OFF;
    }
    
    public void nextChannel() {
        if (mState == POWER_ON) {
            // 下一频道
        } else {
            // 关机状态,不可使用
        }
    }
    
    public void prevChannel() {
        if (mState == POWER_ON) {
            // 上一频道
        } else {
            // 关机状态,不可使用
        }
    }
}
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

示例:利用第2种方式实现的“在不同状态下,电视机的不同行为”。

public interface TvState {
    void nextChannel();
    void prevChannel();
}
1
2
3
4
public class PowerOnState implements TvState {
    
    @Override
    public void nextChannel() {
        // 下一频道
    }
    
    @Override
    public void prevChannel() {
        // 上一频道
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
public class PowerOffState implements TvState {
    
    @Override
    public void nextChannel() {
        // 关机状态,不可使用
    }
    
    @Override
    public void prevChannel() {
        // 关机状态,不可使用
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
public interface PowerController {
    void powerOn();
    void powerOff();
}
1
2
3
4
public class TvController implements PowerController {
    private TvState mTvState;
    
    public void setTvState(TvState tvState) {
        mTvState = tvState;
    }
    
    public void powerOn() {
        setTvState(new PowerOnState());
    }
    
    public void powerOff() {
        setTvState(new PowerOffState());
    }
    
    public void nextChannel() {
        mTvState.nextChannel();
    }
    
    public void prevChannel() {
        mTvState.prevChannel();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24