状态模式是什么?
状态模式(State Design)是一种行为型的设计模式。根据 GoF 对状态录模式的定义:
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
这句话的意思是,允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。简单地说就是当一个对象的状态发生变化时,它的行为也会跟着变化。电视机的开关就是个典型的例子,当电视机是关着的时候,我们按下开关按钮,电视机就会被打开;这时再按下开关按钮,电视机就会被关掉。如果我们要用代码实现这个开关按钮的功能,那么我们一般可能会这么写:
public class SwitchButton {
private State currentState;
public SwitchButton(State currentState) {
this.currentState = currentState;
}
public void push() {
if (State.ON.equals(currentState)) {
currentState = State.OFF;
System.out.println("The television is turned off.");
} else {
currentState = State.ON;
System.out.println("The television is turned on.");
}
}
}
当对象的状态变化逻辑比较简单时,这样的实现是可以的。但在实际的系统开发中,对象的状态变化往往是比较复杂的,这样会产生繁杂的状态判断逻辑(大量的 if-else),不利于系统后期的维护和扩展。在状态模式中,将状态的判断、变化逻辑转移到表示不同状态的一系列类中,这样每一个类代表着一种状态,只专注于这种状态的逻辑,简化了状态判断的处理逻辑,同时也增强的系统的可读性,更利于系统的维护和扩展。
案例
让我们通过一个案例来帮助我们进一步理解状态模式。假设现在有一个在线请假系统,员工可在系统申请休假。员工申请休假时要先填写申请单,内容填写后申请单是处于草稿状态;员工确认填写无误后,就可以提交申请,申请单的状态变为待审批;如果员工取消休假申请,那么申请单则变为关闭状态;经理收到申请单后,可以批准,那么申请单的状态变为完成,整个休假申请的流程就结束了;如果经理不同意员工的休假申请,驳回申请单,那么申请单也会被关闭。下图描述了这个系统中,申请单的状态变化流程。
首先,我们要定义一个抽象的申请单状态来描述这些状态有哪些变化的行为。
public interface LeaveState {
boolean nextStep(Leave leave);
boolean close(Leave leave);
}
接下来是定义各个具体的申请单状态,草稿状态:
public class DraftState implements LeaveState {
@Override
public boolean nextStep(Leave leave) {
leave.setCurrentState(new PendingState());
System.out.println("This leave is pending now.");
return true;
}
@Override
public boolean close(Leave leave) {
leave.setCurrentState(new ClosedState());
System.out.println("This leave is closed now.");
return true;
}
}
待审批状态:
public class PendingState implements LeaveState {
@Override
public boolean nextStep(Leave leave) {
leave.setCurrentState(new CompletedState());
System.out.println("This leave is completed now.");
return true;
}
@Override
public boolean close(Leave leave) {
leave.setCurrentState(new ClosedState());
System.out.println("This leave is closed now.");
return true;
}
}
完成状态:
public class CompletedState implements LeaveState {
@Override
public boolean nextStep(Leave leave) {
System.out.println("This leave has been completed, you can't operate it any more.");
return false;
}
@Override
public boolean close(Leave leave) {
System.out.println("This leave has been completed, you can't operate it any more.");
return false;
}
}
关闭状态:
public class ClosedState implements LeaveState {
@Override
public boolean nextStep(Leave leave) {
System.out.println("This leave has been closed, you can't operate it any more.");
return false;
}
@Override
public boolean close(Leave leave) {
System.out.println("This leave has been closed, you can't operate it any more.");
return false;
}
}
申请单含有发起员工、申请经理、休假时间、休假原因等信息,它的状态变化逻辑则是交由它当前具体的状态控制。
public class Leave {
private Staff initiator;
private Manager approver;
private Date startDate;
private Date endDate;
private String reason;
private LeaveState currentState;
// constructor, getters and setters
public boolean nextStep() {
return currentState.nextStep(this);
}
public boolean close() {
return currentState.close(this);
}
}
Staff 类描述了员工的行为,他可以发起休假申请、提交申请、撤消申请。
public class Staff {
public Leave askForLeave(Date startDate, Date endDate, String reason) {
System.out.println("The staff is asking for leave.");
return new Leave(this, startDate, endDate, reason);
}
public void submitLeave(Leave leave) {
System.out.println("The staff is submitting the leave application.");
leave.nextStep();
}
public void withdrawLeave(Leave leave) {
System.out.println("The staff is withdrawing the leave application.");
leave.close();
}
}
Manager 类描述了经理的行为,他可以批准或驳回员工的休假申请。
public class Manager {
public void approveLeave(Leave leave) {
System.out.println("The manager is approving the leave application.");
if (leave.nextStep()) {
leave.setApprover(this);
}
}
public void disapproveLeave(Leave leave) {
System.out.println("The manager is disapproving the leave application.");
if (leave.close()) {
leave.setApprover(this);
}
}
}
最后,我们通过一个用例来模拟一下员工申请休假、经理批准的情景:
public class StateMain {
public static void main(String[] args) {
Staff staff = new Staff();
Date today = new Date();
Leave leave = staff.askForLeave(today, today, "personal affairs");
staff.submitLeave(leave);
Manager manager = new Manager();
manager.approveLeave(leave);
}
}
案例源码
可在 GitHub 上查看上述案例的完整代码。
参考资料
本文参考的资料如下:
- Head First Design Patterns - Elisabeth Freeman, Chapter 10.
- 《软件秘笈:设计模式那点事》- 郑阿奇,第 21 章。
- https://www.baeldung.com/java-state-design-pattern
- https://refactoring.guru/design-patterns/state
- https://refactoringguru.cn/design-patterns/state
- https://sourcemaking.com/design_patterns/state