1. 状态模式
1.1 简介
State Pattern是属于行为模式,类的行为是基于它的状态改变的。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
特点:
- 主要解决问题:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
- 何时使用:代码中包含大量与对象状态有关的条件语句(如if……else……或者是switch等)。
- 如何解决:将各种具体的状态类抽象出来。
- 关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。
1.2 结构
UML结构图如下:
State Pattern uml.png
模式中的角色:
- 1 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
- 2 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
- 3 具体状态(Concrete State):实现抽象状态定义的接口。
2. 实现
2.1 示例
我们以审批流为例,如果发起一个请假流程,第一节点就是部门领导审核,部门领导审核通过会继续往下走,进入下一个节点,人力资源部门审核,当然人力资源也可以驳回请求,人力资源审核通过之后就可以休假了。
审批节点虚拟类:
public abstract class Node {
private static String name;
public abstract void nodeHandle(FlowContext context);
protected abstract boolean isPermission(String message);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
上下文环境类:
public class FlowContext {
// 流程状态 0:通过 1:退回整改-添加时间 2.退回整改-添加部门 3.已申请 4.结束
private int status;
private String message;
private Node node;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
}
部门直属领导审批节点类:
public class LeadNode extends Node {
@Override
public void nodeHandle(FlowContext context) {
if (context == null || 4 == context.getStatus()) {
System.out.println("无审核内容或流程已经结束");
return;
}
if (3 != context.getStatus()) { // 审核节点校验
System.out.println("无审核内容或非当前节点审批");
return;
}
if (!isPermission(context.getMessage())) {
System.out.println("审批未通过,请加上请假时间");
context.setStatus(1);
context.setNode(new WorkerNode());
context.setStatus(2);
context.getNode().nodeHandle(context);
return;
}
System.out.println(context.getMessage());
setName("张经理");
context.setMessage(context.getMessage() + getName() + "审核通过;");
context.setStatus(0);
context.setNode(new HrNode());
// 调用下个审批节点审批动作,继续审批流
context.getNode().nodeHandle(context);
}
@Override
protected boolean isPermission(String message){
if (StringUtils.isBlank(message)){
return false;
}
if (!message.contains("时间:")){
return false;
}
return true;
}
}
hr审核节点类:
public class HrNode extends Node {
@Override
public void nodeHandle(FlowContext context) {
if (context == null || 4 == context.getStatus()) {
System.out.println("无审核内容或流程已经结束");
return;
}
if (0 != context.getStatus()) { // 审核节点校验
System.out.println("上一节点审批未通过");
return;
}
if (!isPermission(context.getMessage())) {
System.out.println("审批未通过,请补充部门信息");
context.setStatus(2);
context.setNode(new WorkerNode());
context.getNode().nodeHandle(context);
return;
}
setName("HR李");
System.out.println(context.getMessage() + getName() + "审核通过");
// 审核终结不再流转
context.setStatus(4);
context.setEnd(true);
}
@Override
protected boolean isPermission(String message){
if (StringUtils.isBlank(message)){
return false;
}
if (!message.contains("部门:")){
return false;
}
return true;
}
}
员工类:
public class WorkerNode extends Node {
@Override
public void nodeHandle(FlowContext context) {
if (context == null || 4 == context.getStatus()) {
System.out.println("无审核内容或流程已经结束");
return;
}
if (1 == context.getStatus()) {
System.out.println("添加时间");
context.setMessage(context.getMessage()+",时间:两天!");
context.setNode(new LeadNode());
context.setStatus(3);
context.getNode().nodeHandle(context);
return;
}
if (2 == context.getStatus()) {
System.out.println("添加部门");
context.setMessage(context.getMessage()+",部门:市场部!");
context.setNode(new LeadNode());
context.setStatus(3);
context.getNode().nodeHandle(context);
return;
}
}
public void start() {
FlowContext context = new FlowContext();
context.setMessage("请假三天,望审核通过");
Node node = new LeadNode();
context.setNode(node);
context.setStatus(3);
context.getNode().nodeHandle(context); // 发起请求
if (4 == context.getStatus()) {
System.out.println("审核通过,开始休假!");
return;
}
System.out.println("审核未通过,放弃休假或重新申请!");
}
@Override
protected boolean isPermission(String message){
}
}
客户端调用示例类:
public static void main(String[] args) {
WorkerNode node = new WorkerNode();
node.start();
}
2.2 总结
状态模式优点:
- 1:对状态转换规则进行了封装;
- 2:可以使用枚举类,枚举出所有可能的状态。但是需要在枚举状态之前确定状态的种类;
- 3:扩展性好。将所有与某个或者某些状态有关的行为放到了一个类对象中,这样方便管理,并且可以方便的新增状态,只需要改变对象状态就可以实现改变对象行为了;
- 4:代码简洁好维护。状态模式允许状态转换逻辑和状态对象合为一体,而不是一个巨大的条件语句块;
- 5:可以让多个不同的环境对象共享一个状态的对象,这样减少系统中对象的数量。
状态模式缺点:
- 1:增加对象和系统类的个数;
- 2:结构与实现比较复杂,如果使用不当,可能会造成程序结构和代码给人感觉很混乱的;
- 3:对开闭原则支持不好。
网友评论