状态在我们的生活中无处不在。比如,听音乐的时候,我们可以选择不同的播放状态,可以是顺序播放、可以是单曲播放、也可以随机播放等;又如:线上购物后,订单从提交到完成也会有很多的状态变化,如下图所示:(当然中间可能涉及到退货等导致状态变化的,这里不展开说)。
在
再比如:一个TCP连接可以有Open、Acknowledge、Close等状态,一旦状态改变其行为也将发生变化。今天我们就来看一下一个状态改变后行为发生改变的模式 -- 状态模式。
一. 迭代器模式的基本介绍
意图
允许一个对象在其内部状态改变的时候改变它的行为。对象看起来似乎修改了它的类。
结构
状态模式的基本结构如下:
这里涉及到的参与者有如下几种:
- Context(环境)
- 定义客户感兴趣的接口。
- 维护一个ConcreteState子类的实例,这个实例定义当前的状态。
- State(状态)
- 定义一个接口以封装与Context的特定状态相关的行为。
- ConcreteState(具体子类)
- 每个子类实现一个与Context的一个状态相关的行为。
参与者如何协作?
1、Context将与状态相关的请求委托给当前的ConreteState对象处理
2、Context可将自身作为一个参数传递给处理该请求的状态对象。这使得状态对象在必要时可访问Context。
3、Context是客户使用的主要接口。客户可用状态对象来配置一个Context,一旦一个Context配置完毕,它的客户不在需要直接与状态对象打交道。
4、Context或者ConcreteState子类都可以决定哪个状态是另外一个状态的后继者,以及是在何种条件下进行状态转换。
二. 状态模式的示例
接下来以一个2公里慢跑快跑交替训练的例子来实现一个简单的状态模式:
- 抽象状态
package com.wangmengjun.tutorial.designpattern.state;
public abstract class State {
abstract void handle(ExeciseContext context);
}
代码语言:javascript复制package com.wangmengjun.tutorial.designpattern.state;
public class FastRunningState extends State {
@Override
void handle(ExeciseContext context) {
System.out.println("快跑" context.getDistance() "米" );
}
}
代码语言:javascript复制package com.wangmengjun.tutorial.designpattern.state;
public class JoggingState extends State {
@Override
void handle(ExeciseContext context) {
System.out.println("慢跑" context.getDistance() "米" );
}
}
代码语言:javascript复制package com.wangmengjun.tutorial.designpattern.state;
public class BriskWalkingState extends State {
@Override
void handle(ExeciseContext context) {
System.out.println("快走" context.getDistance() "米" );
}
}
代码语言:javascript复制package com.wangmengjun.tutorial.designpattern.state;
public class SlowWalkingState extends State{
@Override
void handle(ExeciseContext context) {
System.out.println("慢走" context.getDistance() "米" );
}
}
- 上下文环境
package com.wangmengjun.tutorial.designpattern.state;
public class ExeciseContext {
private int distance;
private State currentState;
private State joggingState;
private State fastRunState;
private State briskWalkingState;
private State slowWalkingState;
public ExeciseContext() {
joggingState = new JoggingState();
fastRunState = new FastRunningState();
briskWalkingState = new BriskWalkingState();
slowWalkingState = new SlowWalkingState();
}
public void jogging(int distance) {
this.setDistance(distance);
this.setCurrentState(joggingState);
joggingState.handle(this);
}
public void fastRun(int distance) {
this.setDistance(distance);
this.setCurrentState(fastRunState);
fastRunState.handle(this);
}
public void briskWalking(int distance) {
this.setDistance(distance);
this.setCurrentState(briskWalkingState);
briskWalkingState.handle(this);
}
public void slowWalking(int distance) {
this.setDistance(distance);
this.setCurrentState(slowWalkingState);
slowWalkingState.handle(this);
}
/**
* @return the distance
*/
public int getDistance() {
return distance;
}
/**
* @param distance the distance to set
*/
public void setDistance(int distance) {
this.distance = distance;
}
/**
* @return the currentState
*/
public State getCurrentState() {
return currentState;
}
/**
* @param currentState the currentState to set
*/
public void setCurrentState(State currentState) {
this.currentState = currentState;
}
}
简单调用一下:
代码语言:javascript复制package com.wangmengjun.tutorial.designpattern.state;
public class Main {
public static void main(String[] args) {
System.out.println("2公里慢跑 快跑 交替训练 - - - - - - - - - - - -开始");
ExeciseContext context = new ExeciseContext();
context.briskWalking(300);
for(int i =1 ;i<=3;i ) {
context.fastRun(100);
context.jogging(400);
}
context.slowWalking(200);
System.out.println("2公里慢跑 快跑 交替训练 - - - - - - - - - - - -结束");
System.out.println("大汗淋漓,还能坚持~~");
}
}
结果输出:
代码语言:javascript复制2公里慢跑 快跑 交替训练 - - - - - - - - - - - -开始
快走300米
快跑100米
慢跑400米
快跑100米
慢跑400米
快跑100米
慢跑400米
慢走200米
2公里慢跑 快跑 交替训练 - - - - - - - - - - - -结束
大汗淋漓,还能坚持~~
三. 小结
- 状态模式使用场景
在以下情况下可以使用状态模式:
- 一个对象的行为依赖于它所处的状态,对象的行为必须随着状态的改变而改变。
- 对象在某个方法里依赖于一重或者多重的条件转移语句,其中有大量的代码。状态模式把条件转移语句的每一个分支都包装到一个单独的类里。这使得这些条件转移分支能够以类的方式独立存在和演化。维护这些独立的类也就不再影响到系统的其他部分。
- 状态模式 vs. 策略模式
(1)状态模式
将一群行为封装到状态类中,主类的当前状态在状态集合中游走,随着时间的流逝,主类的行为不断变化,但这对客户端而言完全是透明的,而策略模式需要客户端明确所有策略,以指明一个具体的策略。
(2)策略模式
定义一组 算法实现,实现之间可以任意替换,而且可以在运行时动态的选择任意一种实现。 需要客户端清楚所有的策略以选择合适的策略。
如何选择?
状态模式和策略模式很相像,容易混淆,但两者意图不同。有的时候很难区分应当使用状态模式还是应当使用策略模式。一个简单的方法便是考察环境角色是否有明显的状态和状态的过渡。如果环境角色只有一个状态,那么就应当使用策略模式。而状态模式则适用于另一种情况,即环境角色有明显的状态转移。
参考
[1]. 阎宏. Java与模式.电子工业出版社
[2]. Erich Gamma. 设计模式-可复用面向对象软件的基础. 机械工业出版社.