动机
- 一个对象可能会拥有不同的状态,且可在运行时(runtime)在这几个状态之间切换;
- 当这个对象与外界交互时,他的状态就可能发生变化,并在不同状态之间切换;
- 当在状态变化时,对象可表现出不同的行为(以体现状态变化后的影响)。
比如,在一个游戏中,一个游戏角色可处于不同的状态:健康、受伤、死亡:
- 当处于健康状态时,允许其他角色使用武器对其进行射击;
- 当被其他角色射击命中时(生命值下降),处于受伤状态;
- 当生命值下降至0时,处于死亡状态。
同时,当状态变化时,触发相应的行为或逻辑,比如:
- 当处于受伤状态后,允许通过吃食物来增加生命值(设置一个
Boolean值
表示是否允许吃食物,并置为True
); - 当处于死亡状态后,则不会被攻击(或者说被攻击后,生命值不再下降)。
定义
用一句话来表述,状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。
特点
- 一个对象可拥有不同的状态
- 可在不同状态之间不断的切换
- 某一瞬间,只能处于一个特定的唯一状态(而不能同时处于多种状态)
构成
- 一个状态接口(State Interface) ;
- 一个或多个具体的状态角色(Concrete State),且实现了状态接口;
- 状态上下文角色,以允许调用者指定特定的状态;
UML图
实现
环境角色类
public class Context {
//持有一个State类型的对象实例
private State state;
public void setState(State state) {
this.state = state;
}
/**
* 用户感兴趣的接口方法
*/
public void request(String sampleParameter) {
//转调state来处理
state.handle(sampleParameter);
}
}
抽象状态类
public interface State {
/**
* 状态对应的处理
*/
public void handle(String sampleParameter);
}
具体状态类
public class ConcreteStateA implements State {
@Override
public void handle(String sampleParameter) {
System.out.println("ConcreteStateA handle :" + sampleParameter);
}
}
public class ConcreteStateB implements State {
@Override
public void handle(String sampleParameter) {
System.out.println("ConcreteStateB handle :" + sampleParameter);
}
}
客户端类
public class Client {
public static void main(String[] args){
//创建状态
State state = new ConcreteStateB();
//创建环境
Context context = new Context();
//将状态设置到环境中
context.setState(state);
//请求
context.request("test");
}
}
从上面可以看出,环境类Context的行为request()是委派给某一个具体状态类的。通过使用多态性原则,可以动态改变环境类Context的属性State的内容,使其从指向一个具体状态类变换到指向另一个具体状态类,从而使环境类的行为request()由不同的具体状态类来执行。
示例
实现思路
- 定义
Player
类表示游戏角色 - 定义一个
PlayerState
接口 - 定义不同的状态类(
HealthyState
,SurvivalState
,DeadState
),且它们均实现了PlayerState
接口 - 一个上下文类(
GameContext
),并包含一个setState()
方法:
实现
Player.java
public class Player {
public void attack() {
System.out.println("Attack");
}
public void survive() {
System.out.println("Surviving!");
}
public void dead() {
System.out.println("Dead! Game Over");
}
}
PlayerState.java
public interface PlayerState {
void action(Player p);
}
各种状态
public class HealthyState implements PlayerState {
@Override
public void action(Player p) {
p.attack();
p.fireBumb();
p.fireGunblade();
p.fireLaserPistol();
}
}
public class SurvivalState implements PlayerState {
@Override
public void action(Player p) {
p.survive();
p.firePistol();
}
}
public class DeadState implements PlayerState {
@Override
public void action(Player p) {
p.dead();
}
}
GameContext.Java
一个上下文类(GameContext
),并包含一个setState()
方法:
public class GameContext {
private PlayerState state = null;
private Player player = new Player();
public void setState(PlayerState state) {
this.state = state;
}
public void gameAction() {
state.action(player);
}
}
GameTest
public class GameTest {
public static void main(String[] args) {
GameContext context = new GameContext();
context.setState(new HealthyState());
context.gameAction();
System.out.println("*****");
context.setState(new SurvivalState());
context.gameAction();
System.out.println("*****");
context.setState(new DeadState());
context.gameAction();
System.out.println("*****");
}
}
输出
Attack
Fire Bomb
Fire Gunblade
Laser Pistols
*****
Surviving!
Fire Pistol
*****
Dead! Game Over
*****
UML图
待补充
讨论
我们是否可以移除PlayerState
接口和其对应的子类,而简化成只通过if-else if
来实现:
public class GameContext {
private Player player = new Player();
public void gameAction(String state) {
if (state == "healthy") {
player.attack();
player.fireBumb();
player.fireGunblade();
player.fireLaserPistol();
} else if (state == "survival") {
player.survive();
player.firePistol();
} else if (state == "dead") {
player.dead();
}
}
}
从功能实现的角度来说,是没有问题的。
然而,这样的实现违反了开闭原则(Open-Closed Principle),且具有相对较差的可维护性(Maintainability)。
优缺点
Reference
- State Design Pattern in Java
- 《Java与模式》
- 《JAVA与模式》之状态模式 - https://www.cnblogs.com/java-my-life/archive/2012/06/08/2538146.html