Java设计模式之状态模式

转载:https://blog.csdn.net/liaodehong/article/details/52079502

模式简介

        允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类,(State Pattern)是设计模式的一种,属于行为模式。

定义


状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。

模式中的角色

  1 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。

  2 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。

  3 具体状态(Concrete State):实现抽象状态定义的接口。

状态模式的类图


这里来看看状态模式的标准代码;

首先我们先定义一个State抽象状态类,里面定义了一个接口以封装 与Context的一个特定状态相关的行为;

[java]  view plain  copy
  1. /** 
  2.  * 抽象状态类 
  3.  * @author gh 
  4.  * 
  5.  */  
  6. public abstract class State {  
  7.       
  8.     public abstract void Handle(Context context);  
  9. }  
接着再去声明一个ConcreteState具体状态类,每一个子类实现一个与Context的一个状态的相关的行为。

[java]  view plain  copy
  1. public class ConcreteStateA extends State{  
  2.   
  3.     @Override  
  4.     public void Handle(Context context) {  
  5.         context.setState(new ConcreteStateB()); //设置A的下一个状态是B  
  6.           
  7.     }  
  8.   
  9. }  
  10. class ConcreteStateB extends State{  
  11.   
  12.     @Override  
  13.     public void Handle(Context context) {  
  14.         context.setState(new ConcreteStateA()); //设置B的下一个状态是A  
  15.     }  
  16.       
  17. }  
Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态

[java]  view plain  copy
  1. /** 
  2.  * 定义当前的状态 
  3.  * @author gh 
  4.  * 
  5.  */  
  6. public class Context {  
  7.     State state;  
  8.   
  9.     public Context(State state) { //定义Context的初始状态  
  10.         super();  
  11.         this.state = state;  
  12.     }  
  13.   
  14.     public State getState() {  
  15.         return state;  
  16.     }  
  17.   
  18.     public void setState(State state) {  
  19.         this.state = state;  
  20.         System.out.println("当前状态为"+state);  
  21.     }  
  22.     public void request(){  
  23.         state.Handle(this); //对请求做处理并且指向下一个状态  
  24.     }  
  25. }  

提到状态模式,让我想到了工作流,工作流就是控制一个一个的节点状态来实现节点的跳转,最后来控制流程。



如果上面发起了一个请假流程,这个时候第一个节点就是部门领导审核,部门领导审核通过会继续往下走,如果不通过那么有两种状态,一种是直接驳回请求,领导说,项目最近很急,任何人都不能请假,还有一种是你写的请假申请单不对,要退回整改重新写。审核通过后就进入下一个节点,人力资源部门审核,当然人力资源也可以驳回请求,或者要你重新整改,人力资源审核通过之后就可以休假了,这个时候还可以选择是否发送Email。

[java]  view plain  copy
  1. /** 
  2.  * 节点接口 
  3.  * @author gh 
  4.  * 
  5.  */  
  6. public abstract class Node {  
  7.     private static String name; //当前节点名称  
  8.     //节点跳转  
  9.     public abstract void nodeHandle(FlowContext context);  
  10.     public String getName() {  
  11.         return name;  
  12.     }  
  13.     public void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16.       
  17.       
  18. }  
相当于State类,这里维护一个节点名称。

[java]  view plain  copy
  1. /** 
  2.  * 领导节点 
  3.  *  
  4.  * @author gh 
  5.  *  
  6.  */  
  7. public class LeadNode extends Node {  
  8.     @Override  
  9.     public void nodeHandle(FlowContext context) {  
  10.         //根据当前流程的状态,来控制流程的走向  
  11.         //先判断流程是否结束  
  12.         if(!context.isFlag()){  
  13.         System.out.println(context.getMessage()); //先读取申请的内容  
  14.         if(context!=null&&3==context.getStatus()){ //只有出于已经申请的状态才又部门领导审核  
  15.             //设置当前节点的名称  
  16.             setName("张经理");  
  17.             //加上审核意见  
  18.             context.setMessage(context.getMessage()+getName()+"审核通过;");  
  19.             //审核通过  
  20.             context.setStatus(0); //审核通过并指向下一个节点  
  21.             context.setNode(new HrNode());  
  22.             context.getNode().nodeHandle(context);  
  23.         }  
  24.     }else{  
  25.         System.err.println("流程已经结束");  
  26.         }  
  27.     }  
  28. }  

这里创建了一个领导节点,用来维护领导审核的流程,审核通过会交给HR审核;

[java]  view plain  copy
  1. public class HrNode extends Node {  
  2.   
  3.     @Override  
  4.     public void nodeHandle(FlowContext context) {  
  5.         //先判断流程是否结束  
  6.         if(!context.isFlag()){  
  7.         // 根据当前流程的状态,来控制流程的走向  
  8.         if (context != null &&  
  9.                 0 == context.getStatus()) { //只有上一级审核通过后才能轮到HR审核  
  10.             // 设置当前节点的名称  
  11.             setName("HR李");  
  12.             //读取上一级的审核内容并加上自己的意见  
  13.             System.out.println(context.getMessage()+getName()+"审核通过");  
  14.             // 审核通过  
  15.             context.setStatus(0); //HR审核通过并指向下一个节点 ,如果没有下一个节点就把状态设置为终结  
  16.             context.setFlag(true);  
  17.               
  18.         }  
  19.         }else{  
  20.             System.out.println("流程已经结束");  
  21.         }  
  22.     }  
  23.   
  24. }  
这里HR审核通过并把节点设置为完结状态;
[java]  view plain  copy
  1. /** 
  2.  * 流程控制 
  3.  *  
  4.  * @author gh 
  5.  *  
  6.  */  
  7. public class FlowContext {  
  8.     private boolean flag; // 代表流程是否结束  
  9.     /** 
  10.      * 流程状态 0:通过 1:驳回 2.退回整改 3.已申请 
  11.      *  
  12.      */  
  13.     private int status;  
  14.   
  15.     private String message; // 消息  
  16.     private Node node; // 节点信息  
  17.     public boolean isFlag() {  
  18.         return flag;  
  19.     }  
  20.   
  21.     public void setFlag(boolean flag) {  
  22.         this.flag = flag;  
  23.     }  
  24.   
  25.     public int getStatus() {  
  26.         return status;  
  27.     }  
  28.   
  29.     public void setStatus(int status) {  
  30.         this.status = status;  
  31.     }  
  32.   
  33.     public String getMessage() {  
  34.         return message;  
  35.     }  
  36.   
  37.     public void setMessage(String message) {  
  38.         this.message = message;  
  39.     }  
  40.   
  41.     public Node getNode() {  
  42.         return node;  
  43.     }  
  44.   
  45.     public void setNode(Node node) {  
  46.         this.node = node;  
  47.     }  
  48.   
  49.     public static boolean start(FlowContext context) {  
  50.         Node node = new LeadNode();  
  51.         context.setNode(node); // 设置初始节点  
  52.         context.setStatus(3); // 设置状态为申请中  
  53.         context.getNode().nodeHandle(context); // 发起请求  
  54.         // 最后要知道是否申请成功  
  55.         //判断当前是最后一个节点并且审核通过,而且流程结束  
  56.         if("HR李".equals(node.getName())&&0==context.getStatus()&&context.isFlag()){  
  57.             System.out.println("审核通过,流程结束");  
  58.             return true;  
  59.         }else{  
  60.             System.out.println("审核未通过,流程已经结束");  
  61.             return false;  
  62.         }  
  63.     }  
  64.   
  65.     public FlowContext() {  
  66.         super();  
  67.     }  
  68.       
  69. }  

这里维护一个流程控制类,它会在HR和LEAD节点之后传递,并分别由他们去维护各自的节点。

最后写一个测试类测试一下:

[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.             FlowContext context=new FlowContext();  
  3.             context.setMessage("本人王小二,因为十一家里有事情,所以要多请三天假,希望公司能够审核通过");  
  4.             context.start(context);  
  5.               
  6.               
  7.         }  
打印结果如下

本人王小二,因为十一家里有事情,所以要多请三天假,希望公司能够审核通过
本人王小二,因为十一家里有事情,所以要多请三天假,希望公司能够审核通过张经理审核通过;HR李审核通过
审核通过,流程结束;

上面这个例子只是很简单的模仿了一下工作流控制状态的跳转。状态模式最主要的好处就是把状态的判断与控制放到了其服务端的内部,使得客户端不需要去写很多代码判断,来控制自己的节点跳转,而且这样实现的话,我们可以把每个节点都分开来处理,当流程流转到某个节点的时候,可以去写自己的节点流转方法。当然状态模式的缺点也很多,比如类的耦合度比较高,基本上三个类要同时去写,而且会创建很多的节点类。