状态模式--java实现

323 阅读3分钟

App抽奖活动问题

请编写程序完成app抽奖活动,要求如下:

  1. 加入每参加一次活动需要扣除用户50积分,中奖概率是10%。
  2. 奖品数量固定,抽完就不能再抽奖。
  3. 活动有4个状态:可以抽奖、不能抽奖、发放奖品和奖品领完。
  4. 活动的状态关系图如图:

image.png

状态模式的介绍

  • 状态模式(State pattern):它主要用来解决对象在不同状态之间转换时,需要对外输出不同行为的问题。状态和行为是一一对应的,状态之间可以相互转换。
  • 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来是改变了其类。

类图 

image.png 类图说明(即状态模式的角色和职责)

  • Context为环境角色,用于维护State实例,这个实例定义当前状态。
  • State是抽象状态角色,定义一个接口封装与Context的一个特点接口相关行为。
  • ConcreteState具体的状态角色,每个子类实现一个与Context相关的一个状态的行为。

状态模式实现APP抽奖活动

image.png 相关代码:

public abstract class State {
    private RaffleActivity raffleActivity;

    public State(RaffleActivity raffleActivity) {
        this.raffleActivity = raffleActivity;
    }

    public RaffleActivity getRaffleActivity() {
        return raffleActivity;
    }

    /**
     * 扣减积分
     */
    public abstract void deductMoney();

    /**
     * 抽奖
     */
    public abstract void raffle();

    /**
     * 发放奖品
     */
    public abstract void dispensePrize();
}
/**
 * 不能抽奖的状态
 **/
public class NoRaffleState extends State {
    public NoRaffleState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("扣除50积分成功,可以抽奖了");
        getRaffleActivity().setState(getRaffleActivity().getCanRaffleState());
    }

    @Override
    public boolean raffle() {
        System.out.println("扣了积分才能抽奖");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("不能发放奖品");
    }
}
/**
 * 可以抽奖的状态
 **/
public class CanRaffleState extends State {
    public CanRaffleState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("已经扣过积分了,请抽奖");
    }

    @Override
    public boolean raffle() {
        Random random = new Random();
        int randNum = random.nextInt(10);
        if (randNum == 0) {
            getRaffleActivity().setState(getRaffleActivity().getDispenseState());
            return true;
        } else {
            System.out.println("很遗憾,没有抽中奖品,谢谢参与!");
            getRaffleActivity().setState(getRaffleActivity().getNoRaffleState());
            return false;
        }
    }

    @Override
    public void dispensePrize() {
        System.out.println("不能发放奖品");
    }
}
/**
 * 发放奖品的状态
 **/
public class DispenseState extends State {
    public DispenseState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("已经扣过积分了");
    }

    @Override
    public boolean raffle() {
        System.out.println("已经抽过奖了");
        return false;
    }

    @Override
    public void dispensePrize() {
        if (getRaffleActivity().getCount() > 0) {
            System.out.println("恭喜中奖了");
            int count = getRaffleActivity().getCount() - 1;
            getRaffleActivity().setCount(count);
            if (count > 0) {
                getRaffleActivity().setState(getRaffleActivity().getNoRaffleState());
            } else {
                getRaffleActivity().setState(getRaffleActivity().getDispenseOutState());
            }
        } else {
            System.out.println("很遗憾,奖品领完了");
            getRaffleActivity().setState(getRaffleActivity().getDispenseOutState());
        }
    }
}
/**
 * 奖品发放完的状态
 **/
public class DispenseOutState extends State {
    public DispenseOutState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("活动结束");
    }

    @Override
    public boolean raffle() {
        System.out.println("活动结束");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("活动结束");
    }
}

状态模式的注意事项及细节

  1. 代码有很强的可读性。状态模式将每个状态的很为封装到对应的一个类中
  2. 方便维护,将容易产生问题的if/else语句删除了,如果每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产生出很多if-else语句,而且很容易出错。
  3. 符合ocp原则。容易增删状态
  4. 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度。
  5. 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式。