状态模式解析

2020-05-28 22:25:01 浏览数 (1)

状态在我们的生活中无处不在。比如,听音乐的时候,我们可以选择不同的播放状态,可以是顺序播放、可以是单曲播放、也可以随机播放等;又如:线上购物后,订单从提交到完成也会有很多的状态变化,如下图所示:(当然中间可能涉及到退货等导致状态变化的,这里不展开说)。

再比如:一个TCP连接可以有Open、Acknowledge、Close等状态,一旦状态改变其行为也将发生变化。今天我们就来看一下一个状态改变后行为发生改变的模式 -- 状态模式。

一. 迭代器模式的基本介绍

意图

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

结构

状态模式的基本结构如下:

这里涉及到的参与者有如下几种:

  • Context(环境)
    • 定义客户感兴趣的接口。
    • 维护一个ConcreteState子类的实例,这个实例定义当前的状态。
  • State(状态)
    • 定义一个接口以封装与Context的特定状态相关的行为。
  • ConcreteState(具体子类)
    • 每个子类实现一个与Context的一个状态相关的行为。

参与者如何协作?

1、Context将与状态相关的请求委托给当前的ConreteState对象处理

2、Context可将自身作为一个参数传递给处理该请求的状态对象。这使得状态对象在必要时可访问Context。

3、Context是客户使用的主要接口。客户可用状态对象来配置一个Context,一旦一个Context配置完毕,它的客户不在需要直接与状态对象打交道。

4、Context或者ConcreteState子类都可以决定哪个状态是另外一个状态的后继者,以及是在何种条件下进行状态转换。

二. 状态模式的示例

接下来以一个2公里慢跑快跑交替训练的例子来实现一个简单的状态模式:

  • 抽象状态

代码语言:javascript复制
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()  "米" );

  }
}
  • 上下文环境
代码语言:javascript复制
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. 设计模式-可复用面向对象软件的基础. 机械工业出版社.

0 人点赞