author zong
email zongzhe1996@163.com
写在前面
本文主要是作者在学习设计模式过程中的笔记,主要用来记录思路,文中出现的代码并没有在IDE中进行编译运行,只能保证符合作者的思路,并不是最优解决方案。
1.介绍
状态设计模式,是一种行为型设计模式,类的行为基于类的状态进行改变。
2.意图
允许对象在内部状态发生改变的时候,根据状态调整内部行为。
3.解决问题
对象的行为依赖于对象的状态(属性)。
4.何时使用
代码中包含大量与对象状态有关的条件语句(if-else或者switch)。
5.基本结构
状态设计模式的核心,是将类的不同状态与不同状态下的行为单独封装,通过在不同状态下使用不同的类,实现行为的切换。
如图所示, State
接口是状态的顶层接口, State
接口中定义了所有状态的通用接口 doAction()
。AConcreteState
类和 BConcreteState
类分别是 State
接口的两个具体实现类。通过在 AConcreteState
类和 BConcreteState
类中对 doAction()
接口进行不同的实现,为不同状态下的类定义不同的行为。
Context
类为状态的拥有者,负责根据实际情况进行状态的管理包括状态切换,状态获取等。
6.Running Example
通过一个Example,对状态设计模式进行详细说明。
void judgeType(InputContent ic){
if(ic.A.getResult==True){
System.out.println("A is true")
ic.A.run();
}
if(ic.B.getId==1){
System.out.println("B is 1")
ic.B.run();
}
if(ic.C.getContent!=null){
System.out.println("C is not void")
ic.C.run();
}
}
如上所示,函数 judgeType
根据传入参数ic中成员A,B,C的不同情况,分别采取了不同的行为。但是在上述 if-else
的结构中,如果再增加对ic.D的判断,则需要修改源代码,违背了面向对象设计原则中的开闭原则。因此,需要使用状态设计模式对上述代码进行重构。
首先,对 judgeType
中的状态进行抽象,目前包含三个状态:1) A is True. 2) B id is 1. 3) C content is not null. 因此,状态类的结构设计如下:
public interface State{
}
//A is True
public class ATrueState implements State{
}
//B id is 1.
public class BId1State implements State{
}
//C content is not null
public class CContentNoNullState implements State{
}
其次,定义 JudgeType
类,并将状态接口作为 JudgeType
类的内部成员。
public class JudgeType{
private State state;
private InputContent ic;
public JudgeType(){
this.state=new ATrueState();
}
public InputContent getIC(){
return ic;
}
public void setIC(InputContent icIn){
this.ic=icIn;
}
public State getState(){
return this.state;
}
public void setState(State stateIn){
this.state=stateIn;
}
}
然后,对不同状态对应的抽象行为进行抽象,将 ic
作为通过接口 doAction
的传入参数,结果如下:
public interface State{
public void doAction(JudgeType jt);
}
//A is True
public class ATrueState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.A.getResult==True){
System.out.println("A is true")
jt.getIC.A.run();
jt.setState(new BId1State);
}
else{
jt.setState(new BId1State);
}
}
}
//B id is 1.
public class BId1State implements State{
public void doAction(JudgeType jt){
if(jt.getIC.B.getID==1){
System.out.println("B is 1")
jt.getIC.B.run();
jt.setState(new CContentNoNullState);
}
else{
jt.setState(new CContentNoNullState);
}
}
}
//C content is not null
public class CContentNoNullState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.C.getID==1){
System.out.println("C is not void");
jt.getIC.C.run();
System.out.println("Finish!");
}
else{
System.out.println("Finish!");
}
}
}
最后,将主函数中调用函数 JudgeType
的地方调整如下:
void main(InputContent icIn){
JudgeType jt=new JudgeType();
jt.setIC(icIn);
jt.getState().doAction();
}
最后,通过一张执行流程图,展示状态模式的工作流程。
如图所示在, StateDesignPattern
中,在主函数中1创建 JudgeType
对应示例,并通过构造函数设置默认状态为 ATrueState
,然后通过2将计算过程中需要使用的参数 icIn
传入,并通过3调用 getState
接口获取到当前状态,通过调用当前状态的 doAction
执行状态对应的具体行为。然后在状态 ATrueState
中如果if判定条件达成则执行动作,并通过 setState
接口将当前状态设置为 BId1State
,如果if判定条件没有达成,则直接通过 setState
接口将当前状态设置为 BId1State
。如果起始状态为 BId1State
,则采用相同的过程,最后将状态调整为 CContentNoNullState
。
整个执行过程与 if-else
中的执行流程相同,但是,状态设计模式的引入,使得添加新的 if-else
分支不需要改动原有代码,只需要创建一个新的state并实现原有的State接口与行为接口 doAction
即可。
如果添加新分支,对D的name进行判断,在 if-else
中实现这个功能的话,修改如下:
void judgeType(InputContent ic){
if(ic.A.getResult==True){
System.out.println("A is true")
ic.A.run();
}
if(ic.B.getId==1){
System.out.println("B is 1")
ic.B.run();
}
if(ic.C.getContent!=null){
System.out.println("C is not void")
ic.C.run();
}
//新添加的D分支
if(ic.D.getName="new"){
System.out.println("D is a new case")
ic.D.run();
}
}
在 if-else
中添加新分支必须要对原有代码产生修改,而状态设计模式需要进行的调整如下:
//C content is not null
public class CContentNoNullState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.C.getID==1){
System.out.println("C is not void");
jt.getIC.C.run();
//设置下一个新添加的条件为DIsNewState
jt.setState(new DIsNewState);
}
else{
jt.setState(new DIsNewState);
}
}
}
//D is a new case
//为新添加的D分支创建新的状态类
public class DIsNewState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.D.getName=="new"){
System.out.println("D is a new case");
jt.getIC.D.run();
System.out.println("Finish!");
}
else{
System.out.println("Finish!");
}
}
}
但是,从上面的例子可以看到,还是需要对状态 CContentNoNullState
进行一点小修改才能在使用的过程中成功的跳转到状态 DIsNewState
。这里的主要原因是因为我们在最先设计这个案例的过程中忽略了状态的迁移过程,将状态的迁移过程内置在状态当中,使状态迁移过程与状态内部行为的实现过程耦合。针对这个问题,我们将状态的迁移过程单独抽象为一个链表,并将其保存在 JudgeType
类中,以此来实现状态迁移过程与状态内部行为实现的解耦。整个状态设计模式调整如下:
首先,在 JudgeType
类中添加状态链表与状态相关操作,如跳转,添加等。(还可以根据实际需求添加)
public class JudgeType{
private State state;
private InputContent ic;
public JudgeType(){
this.state=new ATrueState();
}
public InputContent getIC(){
return ic;
}
public void setIC(InputContent icIn){
this.ic=icIn;
}
public State getState(){
return this.state;
}
public void setState(State stateIn){
this.state=stateIn;
}
//添加状态链表,并添加相关操作
private List<State> statesList;
private static int stateIndex=0;
public void addState(State stateIn){
this.statesList.add(stateIn);
}
public void toNext(){
stateIndex++;
this.state=stateList.get(i);
}
}
其次,将所有 doAction
中调用的 setState
接口,修改为 toNext
接口。以此实现将具体跳转到某个状态的操作,封装到 toNext
中。
public interface State{
public void doAction(JudgeType jt);
}
//A is True
public class ATrueState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.A.getResult==True){
System.out.println("A is true")
jt.getIC.A.run();
//setState --> toNext
jt.toNext();
}
else{
//setState --> toNext
jt.toNext();
}
}
}
//B id is 1.
public class BId1State implements State{
public void doAction(JudgeType jt){
if(jt.getIC.B.getID==1){
System.out.println("B is 1")
jt.getIC.B.run();
//setState --> toNext
jt.toNext();
}
else{
//setState --> toNext
jt.toNext();
}
}
}
//C content is not null
public class CContentNoNullState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.C.getID==1){
System.out.println("C is not void");
jt.getIC.C.run();
//setState --> toNext
jt.toNext();
}
else{
//setState --> toNext
jt.toNext();
}
}
}
//D is a new case
public class DIsNewState implements State{
public void doAction(JudgeType jt){
if(jt.getIC.D.getName=="new"){
System.out.println("D is a new case");
jt.getIC.D.run();
//setState --> toNext
jt.toNext();
}
else{
//setState --> toNext
jt.toNext();
}
}
}
最后在主函数中,每次添加新的 if-else
分支,都可以创建一个新的状态类,并将对应的状态类示例添加到链表中。
void main(InputContent icIn){
JudgeType jt=new JudgeType();
//添加新分支D
jt.addState(new DIsNewState());
jt.setIC(icIn);
jt.getState().doAction();
}
7.总结
使用状态设计模式取代
if-else
和switch
,需要将不同的条件,封装为不同的状态类,将不同条件对应的行为,封装到不同的状态类中。需要注意的几个要点:
状态管理者/使用者即本文中的
JudgeType
类,要作为每个状态类行为接口doAction
的传入参数,同时状态管理者还要内置状态接口的引用state
。状态管理者可以将内置的所有状态保存在队列,链表一类的数据结构中,并提供相应的操作接口。
将if分支的每次判断封装到对应的状态类中,并在状态类中完成对应行为后,及时切换到对应状态。