设计模式系列—状态模式

开发 前端
本篇和大家一起来学习状态模式相关内容。

 模式定义

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

状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。

模板实现如下:

  1. package com.niuh.designpattern.state.v1; 
  2.  
  3. /** 
  4.  * <p> 
  5.  * 状态模式 
  6.  * </p> 
  7.  */ 
  8. public class StatePattern { 
  9.  
  10.     public static void main(String[] args) { 
  11.         //创建环境   
  12.         Context context = new Context(); 
  13.         //处理请求 
  14.         context.Handle(); 
  15.         context.Handle(); 
  16.         context.Handle(); 
  17.         context.Handle(); 
  18.     } 
  19.  
  20. //抽象状态类 
  21. abstract class State { 
  22.     public abstract void Handle(Context context); 
  23.  
  24. //具体状态A类 
  25. class ConcreteStateA extends State { 
  26.     public void Handle(Context context) { 
  27.         System.out.println("当前状态是 A."); 
  28.         context.setState(new ConcreteStateB()); 
  29.     } 
  30.  
  31. //具体状态B类 
  32. class ConcreteStateB extends State { 
  33.     public void Handle(Context context) { 
  34.         System.out.println("当前状态是 B."); 
  35.         context.setState(new ConcreteStateA()); 
  36.     } 
  37.  
  38. //环境类 
  39. class Context { 
  40.     private State state; 
  41.  
  42.     //定义环境类的初始状态 
  43.     public Context() { 
  44.         this.state = new ConcreteStateA(); 
  45.     } 
  46.  
  47.     //设置新状态 
  48.     public void setState(State state) { 
  49.         this.state = state; 
  50.     } 
  51.  
  52.     //读取状态 
  53.     public State getState() { 
  54.         return (state); 
  55.     } 
  56.  
  57.     //对请求做处理 
  58.     public void Handle() { 
  59.         state.Handle(this); 
  60.     } 

输出结果如下:

  • 当前状态是 A.
  • 当前状态是 B.
  • 当前状态是 A.
  • 当前状态是 B.

解决的问题

对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

 

模式组成

实例说明

实例概况

用“状态模式”设计一个多线程的状态转换程序。

 

分析:多线程存在 5 种状态,分别为新建状态、就绪状态、运行状态、阻塞状态和死亡状态,各个状态当遇到相关方法调用或事件触发时会转换到其他状态,其状态转换规律如下所示:

现在先定义一个抽象状态类(TheadState),然后为上图的每个状态设计一个具体状态类,它们是新建状态(New)、就绪状态(Runnable )、运行状态(Running)、阻塞状态(Blocked)和死亡状态(Dead),每个状态中有触发它们转变状态的方法,环境类(ThreadContext)中先生成一个初始状态(New),并提供相关触发方法,下图所示是线程状态转换程序的结构图:

使用步骤

 

步骤1:定义抽象状态类:线程状态

  1. abstract class ThreadState { 
  2.     //状态名 
  3.     protected String stateName; 

步骤2: 定义具体的状态类

  1. //具体状态类:新建状态 
  2. class New extends ThreadState { 
  3.     public New() { 
  4.         stateName = "新建状态"
  5.         System.out.println("当前线程处于:新建状态."); 
  6.     } 
  7.  
  8.     public void start(ThreadContext hj) { 
  9.         System.out.print("调用start()方法-->"); 
  10.         if (stateName.equals("新建状态")) { 
  11.             hj.setState(new Runnable()); 
  12.         } else { 
  13.             System.out.println("当前线程不是新建状态,不能调用start()方法."); 
  14.         } 
  15.     } 
  16.  
  17. //具体状态类:就绪状态 
  18. class Runnable extends ThreadState { 
  19.     public Runnable() { 
  20.         stateName = "就绪状态"
  21.         System.out.println("当前线程处于:就绪状态."); 
  22.     } 
  23.  
  24.     public void getCPU(ThreadContext hj) { 
  25.         System.out.print("获得CPU时间-->"); 
  26.         if (stateName.equals("就绪状态")) { 
  27.             hj.setState(new Running()); 
  28.         } else { 
  29.             System.out.println("当前线程不是就绪状态,不能获取CPU."); 
  30.         } 
  31.     } 
  32.  
  33. //具体状态类:运行状态 
  34. class Running extends ThreadState { 
  35.     public Running() { 
  36.         stateName = "运行状态"
  37.         System.out.println("当前线程处于:运行状态."); 
  38.     } 
  39.  
  40.     public void suspend(ThreadContext hj) { 
  41.         System.out.print("调用suspend()方法-->"); 
  42.         if (stateName.equals("运行状态")) { 
  43.             hj.setState(new Blocked()); 
  44.         } else { 
  45.             System.out.println("当前线程不是运行状态,不能调用suspend()方法."); 
  46.         } 
  47.     } 
  48.  
  49.     public void stop(ThreadContext hj) { 
  50.         System.out.print("调用stop()方法-->"); 
  51.         if (stateName.equals("运行状态")) { 
  52.             hj.setState(new Dead()); 
  53.         } else { 
  54.             System.out.println("当前线程不是运行状态,不能调用stop()方法."); 
  55.         } 
  56.     } 
  57.  
  58. //具体状态类:阻塞状态 
  59. class Blocked extends ThreadState { 
  60.     public Blocked() { 
  61.         stateName = "阻塞状态"
  62.         System.out.println("当前线程处于:阻塞状态."); 
  63.     } 
  64.  
  65.     public void resume(ThreadContext hj) { 
  66.         System.out.print("调用resume()方法-->"); 
  67.         if (stateName.equals("阻塞状态")) { 
  68.             hj.setState(new Runnable()); 
  69.         } else { 
  70.             System.out.println("当前线程不是阻塞状态,不能调用resume()方法."); 
  71.         } 
  72.     } 
  73.  
  74. //具体状态类:死亡状态 
  75. class Dead extends ThreadState { 
  76.     public Dead() { 
  77.         stateName = "死亡状态"
  78.         System.out.println("当前线程处于:死亡状态."); 
  79.     } 

步骤3:定义环境类

  1. class ThreadContext { 
  2.     private ThreadState state; 
  3.  
  4.     ThreadContext() { 
  5.         state = new New(); 
  6.     } 
  7.  
  8.     public void setState(ThreadState state) { 
  9.         this.state = state; 
  10.     } 
  11.  
  12.     public ThreadState getState() { 
  13.         return state; 
  14.     } 
  15.  
  16.     public void start() { 
  17.         ((New) state).start(this); 
  18.     } 
  19.  
  20.     public void getCPU() { 
  21.         ((Runnable) state).getCPU(this); 
  22.     } 
  23.  
  24.     public void suspend() { 
  25.         ((Running) state).suspend(this); 
  26.     } 
  27.  
  28.     public void stop() { 
  29.         ((Running) state).stop(this); 
  30.     } 
  31.  
  32.     public void resume() { 
  33.         ((Blocked) state).resume(this); 
  34.     } 

输出结果

  • 当前线程处于:新建状态.
  • 调用start()方法-->当前线程处于:就绪状态.
  • 获得CPU时间-->当前线程处于:运行状态.
  • 调用suspend()方法-->当前线程处于:阻塞状态.
  • 调用resume()方法-->当前线程处于:就绪状态.
  • 获得CPU时间-->当前线程处于:运行状态.
  • 调用stop()方法-->当前线程处于:死亡状态.

优点

  1. 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  2. 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  3. 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

缺点

  1. 状态模式的使用必然会增加系统的类与对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

应用场景

通常在以下情况下可以考虑使用状态模式。

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

状态模式的扩展

 

在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享,其结构图如下:

分析:共享状态模式的不同之处是在环境类中增加了一个 HashMap 来保存相关状态,当需要某种状态时可以从中获取,其程序代码如下:

  1. package com.niuh.designpattern.state.v3; 
  2.  
  3. import java.util.HashMap; 
  4.  
  5. /** 
  6.  * <p> 
  7.  * 共享状态模式 
  8.  * </p> 
  9.  */ 
  10. public class FlyweightStatePattern { 
  11.     public static void main(String[] args) { 
  12.         //创建环境  
  13.         ShareContext context = new ShareContext(); 
  14.         //处理请求 
  15.         context.Handle(); 
  16.         context.Handle(); 
  17.         context.Handle(); 
  18.         context.Handle(); 
  19.     } 
  20.  
  21. //抽象状态类 
  22. abstract class ShareState { 
  23.     public abstract void Handle(ShareContext context); 
  24.  
  25. //具体状态1类 
  26. class ConcreteState1 extends ShareState { 
  27.     public void Handle(ShareContext context) { 
  28.         System.out.println("当前状态是: 状态1"); 
  29.         context.setState(context.getState("2")); 
  30.     } 
  31.  
  32. //具体状态2类 
  33. class ConcreteState2 extends ShareState { 
  34.     public void Handle(ShareContext context) { 
  35.         System.out.println("当前状态是: 状态2"); 
  36.         context.setState(context.getState("1")); 
  37.     } 
  38.  
  39. //环境类 
  40. class ShareContext { 
  41.     private ShareState state; 
  42.     private HashMap<String, ShareState> stateSet = new HashMap<String, ShareState>(); 
  43.  
  44.     public ShareContext() { 
  45.         state = new ConcreteState1(); 
  46.         stateSet.put("1", state); 
  47.         state = new ConcreteState2(); 
  48.         stateSet.put("2", state); 
  49.         state = getState("1"); 
  50.     } 
  51.  
  52.     //设置新状态 
  53.     public void setState(ShareState state) { 
  54.         this.state = state; 
  55.     } 
  56.  
  57.     //读取状态 
  58.     public ShareState getState(String key) { 
  59.         ShareState s = (ShareState) stateSet.get(key); 
  60.         return s; 
  61.     } 
  62.  
  63.     //对请求做处理 
  64.     public void Handle() { 
  65.         state.Handle(this); 
  66.     } 

输出结果如下

  • 当前状态是: 状态1
  • 当前状态是: 状态2
  • 当前状态是: 状态1
  • 当前状态是: 状态2

 

源码中的应用

  1. #JDK中的状态模式: 
  2. java.util.Iterator 
  3. # 通过FacesServlet控制, 行为取决于当前JSF生命周期的阶段(状态 
  4. javax.faces.lifecycle.LifeCycle#execute() 

PS:以上代码提交在 Github :

https://github.com/Niuh-Study/niuh-designpatterns.git

责任编辑:姜华 来源: 今日头条
相关推荐

2022-01-12 13:33:25

工厂模式设计

2020-11-03 13:05:18

命令模式

2020-10-23 09:40:26

设计模式

2020-10-19 09:28:00

抽象工厂模式

2013-11-26 15:48:53

Android设计模式SDK

2021-09-29 13:53:17

抽象工厂模式

2022-01-14 09:22:22

设计模式桥接

2021-06-09 08:53:34

设计模式策略模式工厂模式

2021-03-02 08:50:31

设计单例模式

2020-10-21 14:29:15

原型模式

2020-10-20 13:33:00

建造者模式

2021-10-28 19:09:09

模式原型Java

2021-10-26 00:21:19

设计模式建造者

2020-11-09 08:20:33

解释器模式

2020-10-28 11:56:47

桥接模式

2020-11-05 09:38:07

中介者模式

2012-01-13 15:59:07

2012-08-30 09:07:33

设计模式

2013-11-26 16:20:26

Android设计模式

2021-03-05 07:57:41

设计模式桥接
点赞
收藏

51CTO技术栈公众号