行为型设计模式 - 状态模式详解

基本介绍

状态模式(State Pattern)主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题,类的行为是基于它的状态改变的。

模式结构

Context(环境角色):用于维护 State 实例,这个实例定义当前状态

State(抽象状态):定义一个接口以封装与 Context 的一个特定状态相关的行为

ConcreteState(具体状态):每一子类实现一个与 Context 的一个状态相关的行为

举例说明

我们以抽奖的场景作为例子,流程如下:

定义一个抽象状态:State

public interface State {
    RuntimeException EXCEPTION = new RuntimeException("不能执行此操作");

    /**
     * 报名
     */
    default void signUp() {
        throw EXCEPTION;
    }

    /**
     * 抽奖
     */
    default boolean raffle() {
        throw EXCEPTION;
    }

    /**
     * 发奖品
     */
    default void sendPrize() {
        throw EXCEPTION;
    }
}

具体状态1:不能抽奖

public class NotRaffleState implements State {

    private RaffleActivity activity;

    public NotRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    @Override
    public void signUp() {
        //如果没有奖品,直接进入无奖品状态
        if (!activity.hasPrize()) {
            activity.setState(activity.getNoPrizeState());
            return;
        }
        System.out.println("报名成功,可以抽奖了");
        activity.setState(activity.getCanRaffleState());
    }
}

具体状态2:可以抽奖

public class CanRaffleState implements State {

    private RaffleActivity activity;

    public CanRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    @Override
    public boolean raffle() {
        //模拟五分之一的中奖几率
        int n = new Random().nextInt(5);
        if (n == 0) {
            //抽中,进入发放奖品的状态
            System.out.println("恭喜你,抽中了");
            activity.setState(activity.getSendPrizeState());
            return true;
        } else {
            //未抽中,回到初始状态
            System.out.println("很遗憾,没有抽中");
            activity.setState(activity.getNotRaffleState());
            return false;
        }
    }
}

具体状态3:发放奖品

public class SendPrizeState implements State {

    private RaffleActivity activity;

    public SendPrizeState(RaffleActivity activity) {
        this.activity = activity;
    }

    @Override
    public void sendPrize() {
        int count = activity.getCount();
        //没有奖品了
        if (count <= 0) {
            System.out.println("奖品没有了,下次再来吧");
            activity.setState(activity.getNoPrizeState());
        } else {
            activity.setCount(--count);
            System.out.println("奖品已发送");
            activity.setState(activity.getNotRaffleState());
        }
    }
}

具体状态4:奖品领完

public class NoPrizeState implements State {

    private RaffleActivity activity;

    public NoPrizeState(RaffleActivity activity) {
        this.activity = activity;
    }

    @Override
    public void signUp() {
        System.out.println("奖品没有了,下次再来吧");
        System.exit(0);
    }

    @Override
    public boolean raffle() {
        System.out.println("奖品没有了,下次再来吧");
        System.exit(0);
        return false;
    }

    @Override
    public void sendPrize() {
        System.out.println("奖品没有了,下次再来吧");
        System.exit(0);
    }
}

环境角色:抽奖活动

public class RaffleActivity {
    private State state;
    private int count;
    private State notRaffleState = new NotRaffleState(this);
    private State canRaffleState = new CanRaffleState(this);
    private State sendPrizeState = new SendPrizeState(this);
    private State noPrizeState = new NoPrizeState(this);

    public RaffleActivity(int count) {
        this.state = getNotRaffleState();
        this.count = count;
    }

    public void signUp() {
        state.signUp();
    }

    public void raffle() {
        if (state.raffle()) {
            state.sendPrize();
        }
    }

    public State getState() {
        return state;
    }

    public boolean hasPrize(){
        return count > 0;
    }
    //省略getter、setter
}

客户端

public class Client {
    @Test
    public void test(){
        RaffleActivity activity = new RaffleActivity(1);
        for (int i = 0; i < 5; i++) {
            System.out.println("第" + (i + 1) + "次抽奖");
            activity.signUp();
            activity.raffle();
            System.out.println("------------------");
        }
    }
}

运行结果

第1次抽奖
报名成功,可以抽奖了
很遗憾,没有抽中
------------------
第2次抽奖
报名成功,可以抽奖了
恭喜你,抽中了
奖品已发送
------------------
第3次抽奖
奖品没有了,下次再来吧

模式分析

优点:

  • 代码具有较强的可读性。状态模式将每个状态的行为封装到对应的一个类中
  • 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错

缺点:

  • 会产生很多类。每个状态都对应一个类,当状态过多时,维护难度变大
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
  • 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

适用环境:

  • 行为随状态改变而改变的场景

  • 条件、分支语句的代替者

posted @ 2020-05-15 13:46  农夫三拳有点疼~  阅读(376)  评论(0编辑  收藏  举报