行为型模式 - 状态模式(State Pattern)

简介: 行为型模式 - 状态模式(State Pattern)

前言

一、状态模式概述

状态模式定义: 对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

类图如下:

网络异常,图片无法展示
|

代码如下:

public interface ILift {  
   //电梯的4个状态  
   //开门状态  
   public final static int OPENING_STATE = 1;  
   //关门状态  
   public final static int CLOSING_STATE = 2;  
   //运行状态  
   public final static int RUNNING_STATE = 3;  
   //停止状态  
   public final static int STOPPING_STATE = 4;  
   //设置电梯的状态  
   public void setState(int state);  
   //电梯的动作  
   public void open();  
   public void close();  
   public void run();  
   public void stop();  
}  
public class Lift implements ILift {  
   private int state;  
   @Override  
   public void setState(int state) {  
       this.state = state;  
  }  
   //执行关门动作  
   @Override  
   public void close() {  
       switch (this.state) {  
           case OPENING_STATE:  
               System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看  
               this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了  
               break;  
           case CLOSING_STATE:  
               //do nothing //已经是关门状态,不能关门  
               break;  
           case RUNNING_STATE:  
               //do nothing //运行时电梯门是关着的,不能关门  
               break;  
           case STOPPING_STATE:  
               //do nothing //停止时电梯也是关着的,不能关门  
               break;  
      }  
  }  
   //执行开门动作  
   @Override  
   public void open() {  
       switch (this.state) {  
           case OPENING_STATE://门已经开了,不能再开门了  
               //do nothing  
               break;  
           case CLOSING_STATE://关门状态,门打开:  
               System.out.println("电梯门打开了。。。");  
               this.setState(OPENING_STATE);  
               break;  
           case RUNNING_STATE:  
               //do nothing 运行时电梯不能开门  
               break;  
           case STOPPING_STATE:  
               System.out.println("电梯门开了。。。");//电梯停了,可以开门了  
               this.setState(OPENING_STATE);  
               break;  
      }  
  }  
   //执行运行动作  
   @Override  
   public void run() {  
       switch (this.state) {  
           case OPENING_STATE://电梯不能开着门就走  
               //do nothing  
               break;  
           case CLOSING_STATE://门关了,可以运行了  
               System.out.println("电梯开始运行了。。。");  
               this.setState(RUNNING_STATE);//现在是运行状态  
               break;  
           case RUNNING_STATE:  
               //do nothing 已经是运行状态了  
               break;  
           case STOPPING_STATE:  
               System.out.println("电梯开始运行了。。。");  
               this.setState(RUNNING_STATE);  
               break;  
      }  
  }  
   //执行停止动作  
   @Override  
   public void stop() {  
       switch (this.state) {  
           case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)  
               //do nothing  
               break;  
           case CLOSING_STATE://关门时才可以停止  
               System.out.println("电梯停止了。。。");  
               this.setState(STOPPING_STATE);  
               break;  
           case RUNNING_STATE://运行时当然可以停止了  
               System.out.println("电梯停止了。。。");  
               this.setState(STOPPING_STATE);  
               break;  
           case STOPPING_STATE:  
               //do nothing  
               break;  
      }  
  }  
}  
public class Client {  
   public static void main(String[] args) {  
       Lift lift = new Lift();  
       lift.setState(ILift.STOPPING_STATE);//电梯是停止的  
       lift.open();//开门  
       lift.close();//关门  
       lift.run();//运行  
       lift.stop();//停止  
  }  
}

问题分析:

  • 使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
  • 扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑

二、状态模式结构

状态模式包含以下主要角色。

  • 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

三、状态模式案例实现

对上述电梯的案例使用状态模式进行改进。类图如下:

网络异常,图片无法展示
|
代码如下:

//抽象状态类  
public abstract class LiftState {  
   //定义一个环境角色,也就是封装状态的变化引起的功能变化  
   protected Context context;  
   public void setContext(Context context) {  
       this.context = context;  
  }  
   //电梯开门动作  
   public abstract void open();  
   //电梯关门动作  
   public abstract void close();  
   //电梯运行动作  
   public abstract void run();  
   //电梯停止动作  
   public abstract void stop();  
}  
//开启状态  
public class OpenningState extends LiftState {  
   //开启当然可以关闭了,我就想测试一下电梯门开关功能  
   @Override  
   public void open() {  
       System.out.println("电梯门开启...");  
  }  
   @Override  
   public void close() {  
       //状态修改  
       super.context.setLiftState(Context.closeingState);  
       //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作  
       super.context.getLiftState().close();  
  }  
   //电梯门不能开着就跑,这里什么也不做  
   @Override  
   public void run() {  
       //do nothing  
  }  
   //开门状态已经是停止的了  
   @Override  
   public void stop() {  
       //do nothing  
  }  
}  
//运行状态  
public class RunningState extends LiftState {  
   //运行的时候开电梯门?你疯了!电梯不会给你开的  
   @Override  
   public void open() {  
       //do nothing  
  }  
   //电梯门关闭?这是肯定了  
   @Override  
   public void close() {//虽然可以关门,但这个动作不归我执行  
       //do nothing  
  }  
   //这是在运行状态下要实现的方法  
   @Override  
   public void run() {  
       System.out.println("电梯正在运行...");  
  }  
   //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了  
   @Override  
   public void stop() {  
       super.context.setLiftState(Context.stoppingState);  
       super.context.stop();  
  }  
}  
//停止状态  
public class StoppingState extends LiftState {  
   //停止状态,开门,那是要的!  
   @Override  
   public void open() {  
       //状态修改  
       super.context.setLiftState(Context.openningState);  
       //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作  
       super.context.getLiftState().open();  
  }  
   @Override  
   public void close() {//虽然可以关门,但这个动作不归我执行  
       //状态修改  
       super.context.setLiftState(Context.closeingState);  
       //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作  
       super.context.getLiftState().close();  
  }  
   //停止状态再跑起来,正常的很  
   @Override  
   public void run() {  
       //状态修改  
       super.context.setLiftState(Context.runningState);  
       //动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作  
       super.context.getLiftState().run();  
  }  
   //停止状态是怎么发生的呢?当然是停止方法执行了  
   @Override  
   public void stop() {  
       System.out.println("电梯停止了...");  
  }  
}  
//关闭状态  
public class ClosingState extends LiftState {  
   @Override  
   //电梯门关闭,这是关闭状态要实现的动作  
   public void close() {  
       System.out.println("电梯门关闭...");  
  }  
   //电梯门关了再打开,逗你玩呢,那这个允许呀  
   @Override  
   public void open() {  
       super.context.setLiftState(Context.openningState);  
       super.context.open();  
  }  
   //电梯门关了就跑,这是再正常不过了  
   @Override  
   public void run() {  
       super.context.setLiftState(Context.runningState);  
       super.context.run();  
  }  
   //电梯门关着,我就不按楼层  
   @Override  
   public void stop() {  
       super.context.setLiftState(Context.stoppingState);  
       super.context.stop();  
  }  
}  
//环境角色  
public class Context {  
   //定义出所有的电梯状态  
   public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭  
   public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门  
   public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止  
   public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行  
   //定义一个当前电梯状态  
   private LiftState liftState;  
   public LiftState getLiftState() {  
       return this.liftState;  
  }  
   public void setLiftState(LiftState liftState) {  
       //当前环境改变  
       this.liftState = liftState;  
       //把当前的环境通知到各个实现类中  
       this.liftState.setContext(this);  
  }  
   public void open() {  
       this.liftState.open();  
  }  
   public void close() {  
       this.liftState.close();  
  }  
   public void run() {  
       this.liftState.run();  
  }  
   public void stop() {  
       this.liftState.stop();  
  }  
}  
//测试类  
public class Client {  
   public static void main(String[] args) {  
       Context context = new Context();  
       context.setLiftState(new ClosingState());  
       context.open();  
       context.close();  
       context.run();  
       context.stop();  
  }  
}

四、状态模式优缺点

  • 优点:
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 缺点:
  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好。

五、状态模式使用场景

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

后记

喜欢我的文章的朋友点点喜欢、收藏,也欢迎朋友们评论区留下你的意见和建议,恕毅在此拜谢!


相关文章
|
9月前
|
设计模式
设计模式19 - 状态模式【State Pattern】
设计模式19 - 状态模式【State Pattern】
24 0
|
设计模式 Java
Java设计模式-状态模式(State)
Java设计模式-状态模式(State)
|
存储 Java 程序员
行为型模式 - 备忘录模式(Memento Pattern)
行为型模式 - 备忘录模式(Memento Pattern)
|
算法 Java
行为型模式 - 策略模式(Strategy Pattern)
行为型模式 - 策略模式(Strategy Pattern)
|
Java 程序员
行为型模式 - 观察者模式(Observer Pattern)
行为型模式 - 观察者模式(Observer Pattern)
|
设计模式 存储
行为型-State
在实际的软件开发中,状态模式并不是很常用,但是在能够用到的场景里,它可以发挥很大的作用。从这一点上来看,它有点像我们之前讲到的组合模式。 状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。不过,状态机的实现方式有多种,除了状态模式,比较常用的还有分支逻辑法和查表法。今天,我们就详细讲讲这几种实现方式,并且对比一下它们的优劣和应用场景。
135 0
行为型-State
|
设计模式
设计模式实战-状态模式(State Pattern)(中)
设计模式实战-状态模式(State Pattern)(中)
121 0
设计模式实战-状态模式(State Pattern)(中)
|
设计模式 Java
浅谈JAVA设计模式之——状态模式(State)
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
148 0
浅谈JAVA设计模式之——状态模式(State)
|
设计模式 uml
设计模式实战-状态模式(State Pattern)
设计模式实战-状态模式(State Pattern)
196 0
|
C#
C#设计模式之十八状态模式(State Pattern)【行为型】
原文:C#设计模式之十八状态模式(State Pattern)【行为型】 一、引言   今天我们开始讲“行为型”设计模式的第六个模式,该模式是【状态模式】,英文名称是:State Pattern。无论是现实世界,还是面向对象的OO世界,里面都有一个东西,那就是对象。
1604 0