【设计模式实战】状态模式:原理篇

前言

小明开发的应用,账号有登录和未登录状态,现在要求点击个人中心头像时,如果是登录状态时,跳转到个人资料界面,如果是未登录状态时,跳转到登录界面。

小明是这样写的:


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图
【设计模式实战】状态模式:原理篇_第1张图片
State,抽象状态角色:抽象角色,通常使用接口或者抽象类实现。
ConcreteState,具体状态角色:实现了抽象角色的类,从而达到不同状态下的不同行为。
Context,环境角色,持有抽象角色的引用,给客户端调用。

意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

状态模式和策略模式

看下策略模式的UML图:
【设计模式实战】状态模式:原理篇_第2张图片

和状态模式一模一样,如何区分它们呢?

根据场景区分。策略是侧重算法的替换,状态是通过状态改变行为。

策略模式的使用场景:

  1. 诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
  2. 旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
  3. RecyclerView中的 LayoutManager。

状态模式的使用场景:

  1. 打篮球的时候运动员可以有正常状态、不正常状态和超常状态。
  2. 自动售卖机,有四个状态,分别是:没有硬币没有饮料、有硬币没有饮料、有硬币有饮料、没有硬币有饮料。有饮料时投币按按钮分派饮料(没有硬币有饮料<->有硬币有饮料),无饮料时投币按按钮返还硬币(没有饮料没有硬币<->有硬币没有饮料)。还有其他状态的行为,如在有硬币有饮料的时候,在投币,会拒绝,直接返还硬币等。
  3. 订单的各种状态,如待支付、未支付、支付中、支付失败
  4. 任务的各种状态,如待指派、待接受、待完成、已取消
  5. 在游戏系统中,人物有待机、走动、跳跃、释放技能中、死亡等状态,每个状态下的形态都会有所不同

在实际使用的时候,状态之间还有可能互相影响,比如订单在未支付时,点击支付,产品经理要求订单如果是从冻结状态变成待支付状态时,要把该订单放进一个风险列表进行风险监测,那你是不是还得知道该订单上一个状态是否冻结状态。而策略模式不会,策略和策略之间是单独存在的。

你可能感兴趣的:(Android设计模式,状态模式,设计模式,java)