行为型模式 - 状态模式(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();  
  }  
}

四、状态模式优缺点

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

五、状态模式使用场景

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

后记

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


相关文章
|
8天前
|
设计模式 C# C++
中介者模式(Mediator Pattern)
中介者模式是一种行为型设计模式,通过引入中介者对象来简化对象间的通信,减少多对象间的直接交互,降低系统耦合度。核心概念包括中介者、具体中介者和同事类。常见使用场景有聊天室、航空交通控制系统和GUI组件交互。优点是降低系统复杂度和提高灵活性,但中介者可能变得复杂,成为性能瓶颈。
21 3
|
设计模式
设计模式19 - 状态模式【State Pattern】
设计模式19 - 状态模式【State Pattern】
49 0
|
存储 Java 程序员
行为型模式 - 备忘录模式(Memento Pattern)
行为型模式 - 备忘录模式(Memento Pattern)
|
算法 Java
行为型模式 - 策略模式(Strategy Pattern)
行为型模式 - 策略模式(Strategy Pattern)
|
设计模式 存储
行为型-State
在实际的软件开发中,状态模式并不是很常用,但是在能够用到的场景里,它可以发挥很大的作用。从这一点上来看,它有点像我们之前讲到的组合模式。 状态模式一般用来实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。不过,状态机的实现方式有多种,除了状态模式,比较常用的还有分支逻辑法和查表法。今天,我们就详细讲讲这几种实现方式,并且对比一下它们的优劣和应用场景。
158 0
行为型-State
|
设计模式
设计模式实战-状态模式(State Pattern)(中)
设计模式实战-状态模式(State Pattern)(中)
140 0
设计模式实战-状态模式(State Pattern)(中)
|
设计模式 uml
设计模式实战-状态模式(State Pattern)
设计模式实战-状态模式(State Pattern)
232 0
|
C#
C#设计模式之十八状态模式(State Pattern)【行为型】
原文:C#设计模式之十八状态模式(State Pattern)【行为型】 一、引言   今天我们开始讲“行为型”设计模式的第六个模式,该模式是【状态模式】,英文名称是:State Pattern。无论是现实世界,还是面向对象的OO世界,里面都有一个东西,那就是对象。
1648 0
|
C#
C#设计模式(19)——状态者模式(State Pattern)
原文:C#设计模式(19)——状态者模式(State Pattern) 一、引言   在上一篇文章介绍到可以使用状态者模式和观察者模式来解决中介者模式存在的问题,在本文中将首先通过一个银行账户的例子来解释状态者模式,通过这个例子使大家可以对状态者模式有一个清楚的认识,接着,再使用状态者模式来解决上一篇文章中提出的问题。
1090 0