1状态模式概念
1.1 介绍
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。
1.2 定义
状态模式中的行为是由状态来决定,不同的状态下有不同的行为。当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
1.3 使用场景
(1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
(2)代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有大量的多分支语句,且这些分支依赖于该对象的状态。
2 状态模式UML类图通用
状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
我们先来看看状态模式中的3个角色:
(1)State—抽象状态角色:接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。
(2) ConcreteState—具体状态角色:每一个具体状态必须完成两个职责,即是本状态下要做的事情,以及本状态如何过渡到其他状态。
(3) Context—环境角色:定义客户端需要的接口,并且负责具体状态的切换。
3 状态模式通用模板
(1)抽象环境角色
public abstract class State {
// 定义一个环境角色,提供子类访问
protected Context context;
// 设置环境角色
public void setContext(Context _context) {
this.context = _context;
}
// 行为1
public abstract void handle1();
// 行为2
public abstract void handle2();
}
(2)环境角色
public class ConcreteState1 extends State {
@Override
public void handle1() {
// 本状态下必须处理的逻辑
}
@Override
public void handle2() {
// 设置当前状态为stat2
super.context.setCurrentState(Context.STATE2);
// 过渡到state2状态,由Context实现
super.context.handle2();
}
}
public class ConcreteState2 extends State {
@Override
public void handle1() {
// 设置当前状态为state1
super.context.setCurrentState(Context.STATE1);
// 过渡到state1状态,由Context实现
super.context.handle1();
}
@Override
public void handle2() {
// 本状态下必须处理的逻辑
}
}
(3)具体环境角色
public class Context {
public final static State STATE1 = new ConcreteState1();// 定义状态
public final static State STATE2 = new ConcreteState2();
private State CurrentState; // 当前状态
// 获得当前状态
public State getCurrentState() {
return CurrentState;
}
// 设置当前状态
public void setCurrentState(State currentState) {
this.CurrentState = currentState;
this.CurrentState.setContext(this);// 切换状态
}
// 行为委托
public void handle1() {
this.CurrentState.handle1();
}
public void handle2() {
this.CurrentState.handle2();
}
}
(4)客户端
public class Client {
public static void main(String[] args) {
// 定义环境角色
Context context = new Context();
// 初始化状态
context.setCurrentState(new ConcreteState1());
// 行为执行
context.handle1();
context.handle2();
}
}
4 状态模式简单实现
(1)抽象环境角色
public interface TVState {
public void nextChannel();
public void preChannel();
public void turnUp();
public void turnDown();
}
(2)环境角色
public class PowerOff implements TVState {
@Override
public void turnUp() {
}
@Override
public void turnDown() {
}
@Override
public void nextChannel() {
}
@Override
public void preChannel() {
}
}
public class PowerOn implements TVState{
@Override
public void turnUp() {
System.out.println("调高音量");
}
@Override
public void turnDown() {
System.out.println("调低音量");
}
@Override
public void nextChannel() {
System.out.println("下一频道");
}
@Override
public void preChannel() {
System.out.println("上一频道");
}
}
(3)具体环境角色
public interface Controller {
public void turnOn();
public void turnOff();
}
/*
* 电源遥控器
*/
public class TvController implements Controller {
private TVState tvState = new PowerOff();
public void setTate(TVState tvState) {
this.tvState = tvState;
}
@Override
public void turnOn() {
setTate(new PowerOn());
System.out.println("开机啦");
}
@Override
public void turnOff() {
setTate(new PowerOff());
System.out.println("关机啦");
}
public void nextChannel() {
tvState.nextChannel();
}
public void preChannel() {
tvState.preChannel();
}
public void turnUp() {
tvState.turnUp();
}
public void turnDown() {
tvState.turnDown();
}
}
(4)客户端
public class Client {
public static void main(String[] args) {
TvController tvController=new TvController();
tvController.nextChannel();// 未开机,不生效
tvController.turnOn();
tvController.turnUp();
tvController.turnDown();
tvController.nextChannel();
tvController.preChannel();
tvController.turnOff();
tvController.nextChannel();// 已关机,不生效
}
}
(5)结果
5 Android源码中的状态模式
(1)登录系统,根据用户是否登录,判断事件的处理方式。
(2)Wi-Fi管理,在不同的状态下,WiFi的扫描请求处理不一。
6 与策略模式的区别
状态模式与策略模式的结构几乎是一样的,就像是孪生兄弟。但是他们的目地、本质不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立的、可相互替换的。状态模式,通常是自我控制状态的改变。而策略模式,是由外部指定使用什么样的策略。
7 总结
7.1 优点
将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。
7.2 缺点
状态模式的使用必然会增加系统类和对象的个数。
8 参考文章与链接
《Android源码设计模式解析与实战》
《设计模式之禅》