当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化。状态模式是一种对象行为型模式。
本文代码地址:https://github.com/shelimingming/DesignPattern/tree/master/src/main/java/com/sheliming/strategy
一、请假流程
如图,这是一个简化后的请假流程,员工首先提交请假申请到主管审批、主管审批通过后到hr审批、hr审批通过后就到审批完成状态,主管和hr不同意可以将请假单驳回给员工,员工可以修改后重新提交或者终止请假。
二、一般操作
不使用设计模式,首先我们有一个请假申请的类,每个实例代表一个请假记录。
针对每个操作,当请假单的状态不同会对应不同的操作。
package com.sheliming.state.old;
/**
* 不使用设计模式
*/
public class LeaveRequest {
//待提交状态
private static final int TO_COMMIT = 0;
//待主管状态
private static final int TO_MANAGER_CONFIRM = 1;
//待人力资源审批状态
private static final int TO_HR_CONFIRM = 2;
//审批通过状态
private static final int SUCCESS = 3;
//终止状态
private static final int FAILED = 4;
private int state = TO_COMMIT;
/**
* 提交
*/
public void commit() {
if (state == TO_COMMIT) {
System.out.println("提交到主管审核");
state = TO_MANAGER_CONFIRM;
}
}
/**
* 终止
*/
public void stop() {
if (state == TO_COMMIT) {
System.out.println("员工终止申请");
state = FAILED;
}
}
/**
* 同意
*/
public void agree() {
if (state == TO_MANAGER_CONFIRM) {
System.out.println("主管审批通过,到人力资源审批");
state = TO_HR_CONFIRM;
} else if (state == TO_HR_CONFIRM) {
System.out.println("人力资源审批通过,审批完成");
state = SUCCESS;
}
}
/**
* 驳回
*/
public void disagree() {
if (state == TO_MANAGER_CONFIRM) {
System.out.println("主管审批驳回,员工重新提交");
state = TO_COMMIT;
} else if (state == TO_HR_CONFIRM) {
System.out.println("人力资源审批驳回,员工重新提交");
state = TO_COMMIT;
}
}
}
我们来测试一下:
package com.sheliming.state.old;
public class Test {
public static void main(String[] args) {
LeaveRequest leaveRequest = new LeaveRequest();
leaveRequest.commit();
leaveRequest.disagree();
leaveRequest.commit();
leaveRequest.agree();
leaveRequest.agree();
}
}
结果:
提交到主管审核
主管审批驳回,员工重新提交
提交到主管审核
主管审批通过,到人力资源审批
人力资源审批通过,审批完成
看起来还是不错的,每个方法去判断当前的状态,来进行不同的操作。
那么问题来了,现在需要每个请假流程都需要经过部门领导的审批,那么我们需要修改这个类的每个if else来加入领导审批的逻辑。这很明显不符合“开闭原则”!!!
三、使用状态模式
先来一下类图:
首先我们把每个状态抽象出来:
package com.sheliming.state.pattern;
/**
* 状态的接口,各种状态继承该类
*/
public interface State {
/**
* 提交
*/
void commit();
/**
* 终止
*/
void stop();
/**
* 同意
*/
void agree();
/**
* 驳回
*/
void disagree();
}
然后让所有的状态实现这个接口:(如下只举个例子)
package com.sheliming.state.pattern;
/**
* 待提交状态
*/
public class ToCommitState implements State {
private LeaveRequest leaveRequest;
public ToCommitState(LeaveRequest leaveRequest) {
this.leaveRequest = leaveRequest;
}
@Override
public void commit() {
System.out.println("提交到主管审核");
leaveRequest.setState(leaveRequest.getToManagerConfirmState());
}
@Override
public void stop() {
System.out.println("员工终止申请");
leaveRequest.setState(leaveRequest.getFailedState());
}
@Override
public void agree() {
}
@Override
public void disagree() {
}
}
下面是请假申请类:
package com.sheliming.state.pattern;
/**
* 使用状态模式的请假申请类
*/
public class LeaveRequest {
//待提交状态
private State toCommitState;
//待主管状态
private State toManagerConfirmState;
//待人力资源审批状态
private State toHrConfirmState;
//审批通过状态
private State successState;
//终止状态
private State failedState;
//请假单当前的状态
private State state;
public LeaveRequest() {
toCommitState = new ToCommitState(this);
toManagerConfirmState = new ToManagerConfirmState(this);
toHrConfirmState = new ToHrConfirmState(this);
successState = new SuccessState(this);
failedState = new FailedState(this);
state = toCommitState;
}
public void commit() {
state.commit();
}
public void stop() {
state.stop();
}
public void agree() {
state.agree();
}
public void disagree() {
state.disagree();
}
//get and set...
}
还是使用之前的测试类,会得到相同的结果。但是如果现在想加一个部门领导审批的流程,我们只需要新写一个待部门领导审批的状态类,再修改和该状态相关的状态类,而其他的类都不需要修改!
四、总结
其实状态模式就是通过内部状态的改变而改变整个类的行为,这个和策略模式的类图一模一样。(不熟悉策略模式的可以看之前的博客https://blog.csdn.net/u011863006/article/details/89223282)
那么他们的区别是什么呢?
区别就是他们的“意图”不一样。