设计模式——状态模式

本文介绍状态模式。

概念

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

结构和工作原理

  • Context:上下文类,是拥有状态的对象,持有抽象状态类State的实例,该实例定义当前的状态,在具体实现时,它是一个State子类的对象,可以定义初始状态
  • State:抽象状态类,用于定义一个接口以封装与环境类的一个特定状态相关的行为
  • ConcreteState:具体状态类,抽象状态类的子类,每一个子类定义一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同具体状态类其行为有所不同

状态模式描述了对象的状态变化以及对象在不同状态下的不同行为,适用于对象状态需要经常切换且在不同状态下对象的行为不同。

状态模式将对象本身决定状态的属性抽取出来,封装成抽象状态类,并将不同的状态用不同的具体抽象类表示,实现不同状态下的行为,包括状态之间的转换

环境类持有抽象状态类的引用,可以在运行时将任一具体状态对象类设置到环境类中,从而状态类可以改变内部状态随之改变行为,环境类有时可以充当状态管理器的角色,可以在环境类中对状态进行切换操作。

优点:

  • 封装了状态转换的规则
  • 将状态抽取成类,使得增加状态变得方便
  • 允许状态转换逻辑与状态对象合成一体,而不是繁琐冗长的条件语句块
  • 可以让多个环境对象共享

缺点:

  • 状态对象会导致系统类和对象的个数增加
  • 状态模式的结构和实现较为复杂,容易使程序结构和代码混乱
  • 新增状态类时要修改状态转换的代码,违反了开闭原则

因为涉及到环境类状态的设置,那么状态类必然需要与环境类进行通信,一种方式是状态类也持有环境类的引用,另一种方式是通过传递环境类引用给状态类的方法,从面向对象的角度来看,这两者都不是很优雅。

代码示例

public class Process {

private Long processId;

private String processTitle;

private State state;

public Process(Long processId, String processTitle) {
this.processId = processId;
this.processTitle = processTitle;
}

public State getState() {
return state;
}

public void setState(State state) {
state.setProcess(this);
this.state = state;
this.getState().handle();
}
}
public abstract class State {

protected String stateName;

protected Boolean opinion;

protected Process process;

public abstract void handle();

public void setProcess(Process process) {
this.process = process;
}
}
@Slf4j
public class StartState extends State {

private static final String START_STATE = "startState";

public static StartState of() {
return new StartState();
}

@Override
public void handle() {
this.stateName = START_STATE;
this.opinion = true;
log.info("StartState- handle- stateName: {}, opinion:{}", this.stateName, this.opinion);
this.process.setState(LeaderApprovalState.of());
}
}
@Slf4j
public class LeaderApprovalState extends State {

private static final String LEADER_STATE = "leaderApproval";

public static LeaderApprovalState of() {
return new LeaderApprovalState();
}

@Override
public void handle() {
this.stateName = LEADER_STATE;
this.opinion = Math.random() < 0.8;
log.info("LeaderApprovalState- handle- stateName:{}, opinion:{}", this.stateName, this.opinion);
if (opinion) {
this.process.setState(new HumanResourceApprovalState());
} else {
this.process.setState(EndState.ofFalse());
}
}
}
@Slf4j
public class HumanResourceApprovalState extends State {

private static final String HUMAN_RESOURCE_STATE = "humanResourceApproval";

public static HumanResourceApprovalState of() {
return new HumanResourceApprovalState();
}

@Override
public void handle() {
this.stateName = HUMAN_RESOURCE_STATE;
this.opinion = Math.random() < 0.8;
log.info("HumanResourceApprovalState- handle- stateName:{}, opinion:{}", HUMAN_RESOURCE_STATE, opinion);
this.process.setState(EndState.of(opinion));
}
}
@Slf4j
public class EndState extends State {

private static final String END_STATE = "endState";

public static EndState of(Boolean opinion) {
EndState endState = new EndState();
endState.opinion = opinion;
return endState;
}

public static EndState ofFalse() {
EndState endState = new EndState();
endState.opinion = false;
return endState;
}

@Override
public void handle() {
this.stateName = END_STATE;
log.info("EndState- handle- stateName:{}, opinion:{}", this.stateName, this.opinion);
}
}
public class StateClient {

public static void main(String[] args) {
Process process = new Process(1L, "vocation");
process.setState(StartState.of());
}
}

参考资料

https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/state.html
https://juejin.im/post/583e9eb4a22b9d006c23d431

Author: nopainanymore
Link: http://nopainanymore.me/DesignPattern-State/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
wechat