本文共 3781 字,大约阅读时间需要 12 分钟。
状态模式定义:当一个对象内在状态改变时运行其改变行为,这个对象看起来像改变了其类。
在我们if else超过3层的时候,很多时候我们会想到状态模式,其可以使代码结构看起来更清晰也利于扩展,其核心就是封装性,将状态的改变封装起来,客户端不用关心状态的改变,但是实际内部是有状态的转换的。
首先依旧是假定一个场景:我们平常玩网游的时候都会有杀人系统,这里简化一下,玩家分为白名,黄名,红名玩家。白名玩家是不可以杀人和被杀的,但是可以手动开启PK,则会进入黄名状态;黄名状态可以杀别的玩家,杀人后变成红名状态,但是杀人前还是黄名状态时不可以被别的玩家杀害;红名玩家可以杀人和被杀,被杀后状态变为黄名。这就是一个很典型的状态模式的例子,如果不用状态模式,我们在杀人、被杀、状态转换这些方法中都要加if else来判断玩家当前状态进行不同的处理,代码会很难看且臃肿,用状态模式则会清晰很多,下面来看类图:
从图中可以看到,本例分为三种状态WhitePlayer、YellowPlayer、RedPlayer,其只需要管自己状态时内部三个方法的实现即可,下面来看具体实现:
状态接口IState
public interface IState { /** * 击杀其他玩家 */ void killPlayer(); /** * 被其他玩家击杀 */ void beKilled(); /** * 更改状态 */ void transferState();}
玩家状态抽象类PlayerState
public abstract class PlayerState implements IState { protected Context context; public void setContext(Context context) { this.context = context; } @Override public abstract void killPlayer(); @Override public abstract void beKilled(); @Override public abstract void transferState();}
白名玩家实现类WhitePlayer
public class WhitePlayer extends PlayerState { @Override public void killPlayer() { System.out.println("白名玩家不能击杀其他玩家"); } @Override public void beKilled() { System.out.println("白名玩家不可以被其他玩家击杀"); } /** * 白名玩家可以自己手动开启PK模式,状态变为黄名玩家 */ @Override public void transferState() { super.context.setPlayerState(new YellowPlayer()); System.out.println("我开启了PK模式,变成了黄名玩家!"); }}
黄名玩家实现类YellowPlayer
public class YellowPlayer extends PlayerState { /** * 黄名玩家可以击杀其他玩家,击杀后玩家变成红名玩家 */ @Override public void killPlayer() { System.out.println("我杀人啦!!!!!!!"); super.context.setPlayerState(new RedPlayer()); } @Override public void beKilled() { System.out.println("黄名玩家不可以被其他玩家击杀"); } /** * 黄名玩家可以自己关闭PK模式,状态变为白名玩家 */ @Override public void transferState() { super.context.setPlayerState(new WhitePlayer()); System.out.println("我关闭了PK模式,变成了白名玩家!"); }}
红名玩家实现类RedPlayer
public class RedPlayer extends PlayerState { /** * 红名玩家可以击杀其他玩家 */ @Override public void killPlayer() { System.out.println("我杀了很多人啦!!!!"); } /** * 红名玩家可以被其他玩家击杀,击杀后变为黄名 */ @Override public void beKilled() { System.out.println("我被杀了!!!so sad!!!"); super.context.setPlayerState(new YellowPlayer()); } /** * 红名玩家不能主动转换状态 */ @Override public void transferState() { System.out.println("我不能转换自己的状态了!"); }}
场景控制类Context
public class Context implements IState { private PlayerState playerState; public void setPlayerState(PlayerState playerState) { this.playerState = playerState; playerState.setContext(this); } @Override public void killPlayer() { playerState.killPlayer(); } @Override public void beKilled() { playerState.beKilled(); } @Override public void transferState() { playerState.transferState(); }}
下面来看客户端代码,很简单
public static void main(String[] args) { Context context = new Context(); // 玩家初始都是白名玩家 context.setPlayerState(new WhitePlayer()); // 白名玩家尝试杀人 context.killPlayer(); // 不行,我要开启PK杀人 context.transferState(); // 刚开了PK还没杀人呢,就有人想杀我 context.beKilled(); // 我去杀人了 context.killPlayer(); // 有人来杀我了 context.beKilled(); // 被杀了,还是白名安全 context.transferState(); // oh yeah ,不能被杀了 context.beKilled();}
运行结果如下:
白名玩家不能击杀其他玩家我开启了PK模式,变成了黄名玩家!黄名玩家不可以被其他玩家击杀我杀人啦!!!!!!!我被杀了!!!so sad!!!我关闭了PK模式,变成了白名玩家!白名玩家不可以被其他玩家击杀
可以看到,客户端是不关心内部状态的改变的,只需要按照我们平常的操作流程进行操作而已,通篇没有一个if else,代码看起来很清晰。
总结:
优点:
缺点:每个状态对应一个类,状态越多类越多,可能会造成类过多。
欢迎关注个人博客:blog.scarlettbai.com