小明开发的应用,账号有登录和未登录状态,现在要求点击个人中心头像时,如果是登录状态时,跳转到个人资料界面,如果是未登录状态时,跳转到登录界面。
小明是这样写的:
public class AccountManager {
public static final int STATE_UNLOGIN = 0;//未登录状态
public static final int STATE_LOGIN = 1;//已登录状态
private int currentState;//当前状态
public AccountManager() {
}
/**
* 设置状态
*/
public void setCurrentState(int state) {
this.currentState = state;
if (currentState == STATE_UNLOGIN) {
System.out.println("设置为未登录状态");
} else if (currentState == STATE_LOGIN) {
System.out.println("设置为登录状态");
}
}
private void gotoLogin() {
System.out.println("跳转到登录界面");
}
private void gotoPersonInfo() {
System.out.println("跳转到个人中心界面");
}
/**
* 点击头像的操作
*/
public void onClickHeadPortrait() {
System.out.println("点击头像==>");
if (currentState == STATE_UNLOGIN) {
gotoLogin();
} else if (currentState == STATE_LOGIN) {
gotoPersonInfo();
}
}
}
开始测试
public void test() {
AccountManager accountManager = new AccountManager();
//登录
accountManager.setCurrentState(AccountManager.STATE_LOGIN);
accountManager.onClickHeadPortrait();
//退出登录
accountManager.setCurrentState(AccountManager.STATE_UNLOGIN);
accountManager.onClickHeadPortrait();
}
输出结果
设置为登录状态
点击头像==>
跳转到个人中心界面
设置为未登录状态
点击头像==>
跳转到登录界面
小明的领导一看,说这不是和上一次策略模式一样,如果要增加一个冻结状态、黑名单状态,分别要跳到解冻界面和申诉界面,岂不是要改动AccountManager类?AccountManager越来越复杂,充斥着各种if…else,违反开闭原则,扩展性很低。
领导建议这种因为不同状态,而导致不同结果的情景,可以使用状态模式代替。
首先我们需要把状态抽象出来,将它标准化,做成抽象类。
public abstract class IAccountState {
protected AccountContext context;
public AccountContext getContext() {
return context;
}
public void setContext(AccountContext context) {
this.context = context;
}
/**
* 点击头像
*/
public abstract void onClickHeadPortrait();
}
具体类继承抽象类
public class UnLoginState extends IAccountState {
@Override
public void onClickHeadPortrait() {
System.out.println("点击头像跳转到登录界面");
}
}
public class LoginState extends IAccountState {
@Override
public void onClickHeadPortrait() {
System.out.println("点击头像跳转到个人中心界面");
}
}
环境类
public class AccountContext {
public static final IAccountState STATE_LOGIN = new LoginState();
public static final IAccountState STATE_UNLOGIN = new UnLoginState();
public static final IAccountState STATE_FROZEN = new FrozenState();
private IAccountState currentState = STATE_UNLOGIN;
public AccountContext() {
STATE_LOGIN.setContext(this);
STATE_UNLOGIN.setContext(this);
STATE_FROZEN.setContext(this);
}
private void setCurrentState(IAccountState state) {
this.currentState = state;
}
public IAccountState getCurrentState() {
return currentState;
}
/**
* 登录成功
*/
public void loginSuccess() {
setCurrentState(STATE_LOGIN);
}
/**
* 冻结账号
*/
public void frozen() {
setCurrentState(STATE_FROZEN);
}
/**
* 退出登录
*/
public void exit() {
setCurrentState(STATE_UNLOGIN);
}
/**
* 点击头像
*/
public void onClickHeadPortrait() {
currentState.onClickHeadPortrait();
}
}
开始测试
public void test() {
//初始化
AccountContext controller = new AccountContext();
//登录成功
controller.loginSuccess();
controller.onClickHeadPortrait();
//退出登录
controller.exit();
controller.onClickHeadPortrait();
}
输出结果
点击头像跳转到个人中心界面
点击头像跳转到登录界面
当我们想增加一个冻结状态时
public class FrozenState extends IAccountState {
@Override
public void onClickHeadPortrait() {
System.out.println("点击头像跳转到解冻界面");
}
}
public void test() {
//初始化
AccountContext controller = new AccountContext();
//登录成功
controller.loginSuccess();
controller.onClickHeadPortrait();
//冻结账号
controller.frozen();
controller.onClickHeadPortrait();
//退出登录
controller.exit();
controller.onClickHeadPortrait();
}
点击头像跳转到个人中心界面
点击头像跳转到解冻界面
点击头像跳转到登录界面
看一下状态模式的UML图
State,抽象状态角色:抽象角色,通常使用接口或者抽象类实现。
ConcreteState,具体状态角色:实现了抽象角色的类,从而达到不同状态下的不同行为。
Context,环境角色,持有抽象角色的引用,给客户端调用。
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
看下策略模式的UML图:
和状态模式一模一样,如何区分它们呢?
根据场景区分。策略是侧重算法的替换,状态是通过状态改变行为。
策略模式的使用场景:
状态模式的使用场景:
在实际使用的时候,状态之间还有可能互相影响,比如订单在未支付时,点击支付,产品经理要求订单如果是从冻结状态变成待支付状态时,要把该订单放进一个风险列表进行风险监测,那你是不是还得知道该订单上一个状态是否冻结状态。而策略模式不会,策略和策略之间是单独存在的。