1、状态模式的概念
状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
状态模式是对一个对象所处的状态作分析。在不同的状态下表现不同的行为。
状态模式就是对不同状态的行为进行解耦。
2、使用场景
这里还是举大话设计模式的例子。
就拿一个人一天工作的行为来说吧,这个人一天有五种状态,上午、中午、下午、傍晚、晚上各一种。 然后又有个参数是 是否完成工作,如果完成工作,怎晚上能回家,否则晚上要上班。
我们编写一个工作状态类:
public class PersonState {
private int hour;
private boolean isTaskFinished;
public void setHour(int hour) {
this.hour = hour;
}
public void setTaskFinished(boolean taskFinished) {
isTaskFinished = taskFinished;
}
public void work() {
if (hour < 12) {
System.out.println("上午状态可以");
} else if (hour < 13) {
System.out.println("中午有点小困");
} else if (hour < 17) {
System.out.println("下午半睡半醒");
} else {
if (isTaskFinished) {
System.out.println("下班回家咯");
} else {
if (hour < 21) {
System.out.println("加班,有点恶心");
} else {
System.out.println("直接在公司睡了");
}
}
}
}
}
根据状态来执行不同的行为,然后我们在main函数的代码如下:
public void main() {
PersonState personState = new PersonState();
personState.setHour(10);
personState.work();
personState.setHour(12);
personState.work();
personState.setHour(14);
personState.work();
personState.setHour(17);
personState.setTaskFinished(false);
personState.work();
personState.setHour(19);
personState.work();
personState.setHour(22);
personState.work();
}
虽然功能是完成了,但是其实这样的代码还是有许多漏洞的。
比如我们加一个需求,要求就算要加班,21点到了也必须要下班走人,而这个时候更改代码就要更改整个work方法
,这样是显然的违反了 开放-封闭原则
,而这个类通过if/else
分支语句做了很多的事情,其实也是违背了 单一职责原则
我们可以通过UML图来描述一下状态模式
3、UML图
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
Handler()
方法是关键,通过这个方法可以指向下一个状态。
我们先写一个模板出来:
首先是State类,它定义了一个处理状态的方法
public abstract class State {
public abstract void Handler(Context context);
}
然后实现这个Context类,它里面维护了一个State实例:
public class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.Handler(this);
}
}
然后实现具体的每个状态类A、B:
public class ConcreteStateA extends State {
@Override
public void Handler(Context context) {
context.setState(new ConcreteStateB());
}
}
class ConcreteStateB extends State {
@Override
public void Handler(Context context) {
context.setState(new ConcreteStateA());
}
}
最后客户端的代码如下所示:
public void main() {
Context c = new Context(new ConcreteStateA());
//不断更新状态
c.request();
c.request();
c.request();
c.request();
}
通过执行request()
实现了状态的更新,而且这个代码解耦度高,利于扩展。
我们将这个UML图代入到上面那个例子中去。
这样写代码的可扩展度就高了。
具体的代码我就不写了有了这个图,代码还不好写吗。
4、特点
- 将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中。所以通过定义新的子类可以很容易地增加新的状态和转换。
- 状态模式通过把各种状态转移逻辑分布到了State的子类之间,来减少相互的依赖。
- 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用到状态模式了。