状态模式
上文我们讲到了策略模式,本文呢,我们来一起认识一下策略模式的双胞胎兄弟:状态模式。
状态模式(State Pattern),当一个对象的内在状态改变时允许改变其行为,这个对象看起来就像改变了其类。
我第一遍读到这个定义的时候,简直是一脸懵逼状态,不知道他在说什么,一句一句研究吧,当一个对象的内在状态改变时允许改变其行为,因为这个模式将状态封装为独立
的类,并将动作委托到代表当前状态的类,所以,对象的行为会随着内在状态的改变而改变;这个对象看起来就像改变了其类,从客户的视角来看:如果说你使用的对象能够
完全改变它的行为,那么你会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象。
来看下状态模式的类图吧:
一看,跟策略模式的类图结构完全没差啊,但是两者在意图上是有着差别的,我们来看一个状态模式的例子,然后再去说明状态模式与策略模式有哪些区别吧。
任务管理软件大家都了解吧,任务有未启动、进行中、已解决、已关闭等状态,我们可以对任务进行启动任务、录入工作日志、提出解决方案、关闭任务等操作,假如,我们对
任务进行启动操作,那么任务处于不同状态下就会产生不同的反应。
这个操作的代码流程怎么实现?首先,我是这样实现的:
package com.hirain.chc.state;
public class Task {
//定义任务的几种状态
public static final int NOT_START = 0; //未启动
public static final int UNDERWAY = 1; //运行中
public static final int RESOLVED = 2; //已解决
public static final int CLOSED = 3; //已关闭
//指定当前状态
public int state = NOT_START;
/**
* 启动操作.
*/
public void startUp(){
if (state == 0) {
System.out.println("启动成功,任务切换到运行中状态了");
state = 1;
} else if (state == 1) {
System.out.println("启动失败,当前任务正处于运行中状态");
} else if (state == 2) {
System.out.println("启动失败,当前任务正处于已解决状态");
} else if (state == 3) {
System.out.println("启动失败,当前任务正处于已关闭状态");
}
}
/**
* 录入工作内容操作.
*/
public void writeContent() {
if (state == 0) {
System.out.println("写入失败,任务尚未启动");
state = 1;
} else if (state == 1) {
System.out.println("写入成功");
} else if (state == 2) {
System.out.println("写入失败,任务已经处于已解决状态");
} else if (state == 3) {
System.out.println("写入失败,任务已经处于已关闭状态");
}
}
/**
* 提出解决方案操作.
*/
public void solve () {
if (st ate == 0) {
System.out.println("操作失败,任务尚未启动");
state = 1;
} else if (state == 1) {
System.out.println("操作成功,任务已更新至已解决状态");
state = 2;
} else if (state == 2) {
System.out.println("操作失败,任务已经处于已解决状态");
} else if (state == 3) {
System.out.println("操作失败,任务已经处于已关闭状态");
}
}
/**
* 关闭任务操作.
*/
public void close() {
if (state == 0) {
System.out.println("操作失败,任务尚未启动");
state = 1;
} else if (state == 1) {
System.out.println("操作成功,任务正处于运行中状态,不可关闭");
state = 2;
} else if (state == 2) {
System.out.println("操作成功,任务已经更新至已关闭状态");
state = 3;
} else if (state == 3) {
System.out.println("操作失败,任务已经处于已关闭状态");
}
}
}
嗯,初步看来这个类满足了我们的需求,但是,如果我们某一天要新增一种状态呢,该怎么修改代码以适应我们的需求?
挂起状态:运行中的任务可切换至挂起状态,表示任务需要暂时暂停。
如果基于上面那个代码来更改的话,那么我们需要增加一个变量,然后再每个方法中再增加一种判断条件,代码修改量太大,容易出现bug引起整个系统的不稳定。
所以,这种实现方式是不可行的,我们用状态模式来解决一下这个问题,按照状态模式的类图来设计一下这个代码:
package com.hirain.chc.state.demo;
import com.hirain.chc.state.demo.state.ClosedState;
import com.hirain.chc.state.demo.state.ITaskState;
import com.hirain.chc.state.demo.state.NotStartState;
import com.hirain.chc.state.demo.state.ResolvedState;
import com.hirain.chc.state.demo.state.UnderwayState;
public class Task {
public ITaskState notStartState;
public ITaskState underwayState;
public ITaskState resolvedState;
public ITaskState closedState;
/**
* 当前状态.
*/
public ITaskState state;
/**
* 构造方法内初始化所有的状态.
*/
public Task() {
notStartState = new NotStartState(this);
underwayState = new UnderwayState(this);
resolvedState = new ResolvedState(this);
closedState = new ClosedState(this);
state = notStartState;
}
/**
* 启动操作.
*/
public void startUp() {
state.startUp();
}
/**
* 录入工作内容.
*/
public void writeContent() {
state.writeContent();
}
/**
* 提出解决方案操作.
*/
public void solve () {
state.solve();
}
/**
* 关闭任务操作.
*/
public void close() {
state.close();
}
public ITaskState getState() {
return state;
}
public void setState(ITaskState state) {
this.state = state;
}
public ITaskState getNotStartState() {
return notStartState;
}
public void setNotStartState(ITaskState notStartState) {
this.notStartState = notStartState;
}
public ITaskState getUnderwayState() {
return underwayState;
}
public void setUnderwayState(ITaskState underwayState) {
this.underwayState = underwayState;
}
public ITaskState getResolvedState() {
return resolvedState;
}
public void setResolvedState(ITaskState resolvedState) {
this.resolvedState = resolvedState;
}
public ITaskState getClosedState() {
return closedState;
}
public void setClosedState(ITaskState closedState) {
this.closedState = closedState;
}
}
package com.hirain.chc.state.demo.state;
public interface ITaskState {
public void startUp();
public void writeContent();
public void solve();
public void close ();
}
package com.hirain.chc.state.demo.state;
import com.hirain.chc.state.demo.Task;
public class NotStartState implements ITaskState {
Task task;
public NotStartState(Task task) {
this.task = task;
}
@Override
public void startUp() {
task.setState(task.getUnderwayState());
System.out.println("启动成功,任务 已更新至运行中状态");
}
@Override
public void writeContent() {
System.out.println("写入失败,任务尚未启动");
}
@Override
public void solve() {
System.out.println("操作失败,任务尚未启动");
}
@Override
public void close() {
System.out.println("操作失败,任务尚未启动");
}
}
package com.hirain.chc.state.demo.state;
import com.hirain.chc.state.demo.Task;
public class UnderwayState implements ITaskState {
Task task;
public UnderwayState(Task task) {
this.task = task;
}
@Override
public void startUp() {
System.out.println("操作失败,任务已处于运行中状态");
}
@Override
public void writeContent() {
System.out.println("写入成功");
}
@Override
public void solve() {
System.out.println("操作成功,任务已更新至已解决状态");
task.setState(task.getResolvedState());
}
@Override
public void close() {
System.out.println("操作失败,任务正处于运行中状态");
}
}
package com.hirain.chc.state.demo.state;
import com.hirain.chc.state.demo.Task;
public class ResolvedState implements ITaskState {
Task task;
public ResolvedState(Task task) {
this.task = task;
}
@Override
public void startUp() {
System.out.println("操作失败,任务已处于已解决状态");
}
@Override
public void writeContent() {
System.out.println("操作失败,任务已处于已解决状态");
}
@Override
public void solve() {
System.out.println("操作失败,任务已处于已解决状态");
}
@Override
public void close() {
System.out.println("操作成功,任务已更新至已关闭状态");
task.setState(task.getClosedState());
}
}
package com.hirain.chc.state.demo.state;
import com.hirain.chc.state.demo.Task;
public class ClosedState implements ITaskState {
Task task;
public ClosedState(Task task) {
this.task = task;
}
@Override
public void startUp() {
System.out.println("操作失败,任务已处于已关闭状态");
}
@Override
public void writeContent() {
System.out.println("操作失败,任务已处于已关闭状态");
}
@Override
public void solve() {
System.out.println("操作失败,任务已处于已关闭状态");
}
@Override
public void close() {
System.out.println("操作失败,任务已处于已关闭状态");
}
}
测试类:
package com.hirain.chc.state.demo;
/**
* 客户端应用
*
* @author
*
*/
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
Task task = new Task();
task.close();
task.startUp();
task.close();
task.solve();
task.close();
}
}
这就是按照状态模式来实现的,虽然类变多了,但是减少了复杂的if else ,同时程序也更容易拓展了,如果我们现在要增加一种"挂起"状态,那么需要更改的地方就是Task类
的变量与构造方法,然后增加一个"挂起"状态类就可以了。虽然对开闭原则的支持不是很好,但相比之下还是比较容易接受的。
优缺点分析:
优点:
将与特定状态相关的行为局部化,并且将不同状态的行为分割开来,符合类的单一职责原则。
消除了复杂的if else判断语句。
缺点:
增加了类的个数。
对开闭原则的支持并不好,新增状态类的时候需要修改负责状态转换部分的源码。
状态模式与策略模式的比较:
状态模式将各个状态所对应的行为分离开来,即对于不同的状态,由不同的子类负责具体操作,不同状态的切换由子类实现;
而策略模式是直接依赖注入到Context类的参数进行选择策略,不存在切换状态的操作。