状态模式即允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类,换句话说状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。
(1)、Context:上下文,定义客户端可能调用的接口,并且保留一个具体状态类的实例。这个具体状态类的实例给出此环境对象的现有状态。
(2)、State:定义一个接口,用以封装环境对象的一个特定的状态所对应的行为。
(3)、ConcreteState:每一个具体状态类都实现了环境(Context)的一个状态所对应的行为。
before :
class ElectricFanControl {
private int currentState;
public ElectricFanControl() {
currentState = 0;
}
public void pull() {
if (currentState == 0) {
currentState = 1;
System.out.println("low speed");
} else if (currentState == 1) {
currentState = 2;
System.out.println("medium speed");
} else if (currentState == 2) {
currentState = 3;
System.out.println("high speed");
} else {
currentState = 0;
System.out.println("turning off");
}
}
}
public class StateDemo {
public static void main(String[] args) {
ElectricFanControl electricFanControl = new ElectricFanControl();
while (true) {
System.out.print("Press ENTER");
getLine();
electricFanControl.pull();
}
}
static String getLine() {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = null;
try {
line = in.readLine();
} catch (IOException ex) {
ex.printStackTrace();
}
return line;
}
}
after:
//State接口:
public interface State {
public void start(Context context);
public void close(Context context);
}
//StartState 类:
class StartState implements State {
@Override
public void start(Context context) {
System.out.println("do noting");
}
@Override
public void close(Context context) {
System.out.println("close State");
context.setState(new CloseState());
}
}
class CloseState implements State {
@Override
public void start(Context context) {
System.out.println("start State");
context.setState(new StartState());
}
@Override
public void close(Context context) {
System.out.println("do noting");
}
}
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void start() {
getState().start(this);
}
public void close() {
getState().close(this);
}
}
public class StatePatternDemo {
public static void main(String... args) {
Context context = new Context();
// 初始为开始状态
context.setState(new StartState());
// 切换为关闭状态
context.close();
// 切换为开始状态
context.start();
}
}
控制对象状态转换的条件逻辑过于复杂,用处理特殊状态和状态转换的state类替换条件语句
优点:
简化复杂的状态改变逻辑,有利于代码的阅读、维护和扩展。
缺点:
状态类增加,设计复杂度提高
状态机(状态模式的一种应用)在工作流或游戏等各种系统中有大量使用,如各种工作流引擎,它几乎是状态机的子集和实现,封装状态的变化规则。Spring状态机帮助开发者简化状态机的开发过程,让状态机结构更加层次化。
如下代码显示如何用Spring状态机来实现一个洗衣机的工作流程:
定义状态和事件枚举类:
public enum States {
RUNNING, HISTORY, END,
WASHING, RINSING, DRYING,
POWEROFF
}
public enum Events {
RINSE, DRY, STOP,
RESTOREPOWER, CUTPOWER
}
状态机配置:状态
通过多次调用withStates() 定义分层state, 你可以使用parent() 指定这些特定state是其他state的子state。
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.RUNNING)
.state(States.POWEROFF)
.end(States.END)
.and()
.withStates()
.parent(States.RUNNING)
.initial(States.WASHING)
.state(States.RINSING)
.state(States.DRYING)
.history(States.HISTORY, History.SHALLOW);
}
状态机配置:状态转化
三种不同类型的转换:外部转换、内部转换和本地转换。转换要么由信号(发送到状态机的事件)触发,要么由计时器触发
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.WASHING).target(States.RINSING)
.event(Events.RINSE)
.and()
.withExternal()
.source(States.RINSING).target(States.DRYING)
.event(Events.DRY)
.and()
.withExternal()
.source(States.RUNNING).target(States.POWEROFF)
.event(Events.CUTPOWER)
.and()
.withExternal()
.source(States.POWEROFF).target(States.HISTORY)
.event(Events.RESTOREPOWER)
.and()
.withExternal()
.source(States.RUNNING).target(States.END)
.event(Events.STOP);
}
后续:与策略模式的比较
同:
1、子类的使用:状态和策略模式都通过状态/策略的不同派生子类来更改具体实现。
2、模式类图:状态模式和策略模式之间最大的相似性之一是它们的类图,除了类名之外,它们看起来几乎相同。这两种模式都定义了状态/策略基类,子状态/子策略都继承基类。
3、两者都遵循开闭原则:状态模式的Context是对修改关闭的,即关于状态如何被访问和使用的逻辑是固定的。但是各个状态是开放的,也就是说,可以通过扩展可以添加更多的状态。类似地,策略模式的context是对修改关闭的,但是各个策略的子类是开放可扩展的。
异:
1、模式意图:策略模式的意图或目的是拥有一系列可互换的算法,这些算法可以根据context和/或客户需求进行选择。而状态模式的目的是管理对象的状态以及对象的行为,对象的行为会随着状态的变化而变化。
2、客户端对策略/状态的感知:在策略模式实现中,所选择的策略依赖于客户端,因此客户端知道使用的是哪种策略。而在状态模式实现中,客户端与context交互以对对象进行操作,但不决定选择哪种状态。对象本身似乎根据客户端通过context进行的交互来更改其状态类。
3、context的引用:状态模式中的每个状态都持有context的引用。但是,策略模式中每个策略并不持有context的引用。
4、状态/策略之间的关系:状态模式中的不同状态彼此相关,例如作为前一个或者后一个状态等。这是因为在状态之间像有限状态机有一个流动。然而,策略模式只是从多个可用策略中选择一个策略,策略之间没有后者/前者的关系。
5、怎样做/什么&何时做:多种策略定义了做某事的多种方式。而多个状态定义要做什么,并基于状态之间的关系定义何时做。