设计模式--状态模式

案例

APP抽奖活动

  1. 假如没参加一次这个活动要扣除用户50积分,中奖概率是10%
  2. 奖品数量固定,抽完就不能再抽奖
  3. 活动有4个状态:可以抽奖,不能抽奖,发放奖品和奖品领完
  4. 有3个活动:扣除50积分,抽奖,发放奖品
  5. 4个状态转换图如下
    状态转换图
传统解决方案

通过if/else判断当前抽奖的状态,从而实现不同的逻辑,伪代码如下

if(state == 不能抽奖){
	扣除50积分();
	state = 可以抽奖;
}else if(state == 可以抽奖){
	抽奖();
	if(抽中了){
		state = 不能抽奖;
	}else{
		state = 发放奖品;
	}
}else if(state == 发放奖品){
	发放奖品();
	奖品数量减一;
	if(当前所剩奖品数量不为0){
		state = 不能抽奖;
	}else{
		state = 奖品领完;
	}
}else if(state == 奖品领完){
	打印提示信息;
}else{
	打印出错信息;
}
传统方案的问题

难以应对变化,在添加一种状态时,我们需要手动添加if/else,在添加一种功能时,要对所有的状态进行判断,这样把每个状态对应的活动代码全部挤在一个类里,会使得代码变得越来越臃肿,难以维护,并且一旦没有处理某个状态。便会产生及其严重的BUG。

用状态模式解决
  1. 画出状态图,上面已经画过了
  2. 分析有多少个活动,将这些活动封装在一个状态接口里,每个具体的状态可以在各自的状态下实现这些活动
  3. 分析有多少个具体状态,实现状态接口,并聚合上下文客户端
  4. 创建一个上下文客户端,组合或聚合状态接口作为当前状态的引用;实现状态接口,使其也拥有状态接口的活动;组合所有的具体状态

状态模式

/**
 * 
 * @ClassName StatePattern
 * @Description 状态模式
 * @author fuling
 * @date 2020年11月3日 下午3:09:44
 */
public class StatePattern {
	public static void main(String[] args) {
		Context context = new Context(1);
		for(int i = 0; i < 20; i++) {
			System.out.println("=========================");
			context.deductPoints();
			context.luckyDraw();
		}
	}
}

class Context implements State{
	
	//组合所有的状态类
	static State CANNOT_LUCKY_DRAW_STATE;//不能抽奖状态
	static State CAN_LUCKY_DRAW_STATE;//可以抽奖状态
	static State GIVE_OUT_PRIZE_STATE;//发放奖品状态
	static State PRIZE_EMPTY_STATE;//奖品领完状态
	
	//当前拥有的奖品数量
	int count;
	//当前状态
	State currentState;
	Context(int count){
		this.count = count;
		CANNOT_LUCKY_DRAW_STATE = new CannotLuckyDrawState(this);
		CAN_LUCKY_DRAW_STATE = new CanLuckyDrawState(this);
		GIVE_OUT_PRIZE_STATE = new GiveOutPrizeState(this);
		PRIZE_EMPTY_STATE = new PrizeEmptyState(this);
		
		//如果当前奖品数量大于0,则初始化当前状态为不能抽奖状态,否则为奖品领完状态
		if(count > 0)currentState = CANNOT_LUCKY_DRAW_STATE;
		else currentState = PRIZE_EMPTY_STATE;
	}
	@Override
	public void deductPoints() {
		this.currentState.deductPoints();
		
	}
	@Override
	public boolean luckyDraw() {
		if(this.currentState.luckyDraw()) {
			giveOutPrize();
			return true;
		}
		return false;
	}
	@Override
	public void giveOutPrize() {
		this.currentState.giveOutPrize();
		
	}
}

interface State{
	
	RuntimeException EXCEPTION = new UnsupportedOperationException();
	
	//定义三个活动
	void deductPoints();//扣除积分
	boolean luckyDraw();//抽奖
	void giveOutPrize();//领取奖品
}

/**
 * 
 * @ClassName CannotLuckyDrawState
 * @Description 不能抽奖状态
 * @author fuling
 * @date 2020年11月3日 下午3:57:58
 */
class CannotLuckyDrawState implements State{
	
	Context context;
	
	CannotLuckyDrawState(Context context){
		this.context = context;
	}

	@Override
	public void deductPoints() {
		System.out.println("成功扣除50积分!");
		//当前状态变为可以抽奖状态
		this.context.currentState = Context.CAN_LUCKY_DRAW_STATE;
	}

	@Override
	public boolean luckyDraw() {
		throw EXCEPTION;
	}

	@Override
	public void giveOutPrize() {
		throw EXCEPTION;
		
	}


	
	
}

/**
 * 
 * @ClassName CanLuckyDrawState
 * @Description 可以抽奖状态
 * @author fuling
 * @date 2020年11月3日 下午3:58:12
 */
class CanLuckyDrawState implements State{
	
	Context context;
	
	CanLuckyDrawState(Context context){
		this.context = context;
	}

	@Override
	public void deductPoints() {
		throw EXCEPTION;
		
	}

	@Override
	public boolean luckyDraw() {
		int random = new Random().nextInt(10);
		if(random != 9) {
			System.out.println("很遗憾,没有中奖!");
			this.context.currentState = Context.CANNOT_LUCKY_DRAW_STATE;
			return false;
		}else {
			System.out.println("中奖了!");
			this.context.currentState = Context.GIVE_OUT_PRIZE_STATE;
			return true;
		}
		
	}

	@Override
	public void giveOutPrize() {
		throw EXCEPTION;
		
	}

	
	
}

/**
 * 
 * @ClassName GiveOutPrizeState
 * @Description 领取奖品状态
 * @author fuling
 * @date 2020年11月3日 下午3:58:24
 */
class GiveOutPrizeState implements State{
	
	Context context;
	
	GiveOutPrizeState(Context context){
		this.context = context;
	}

	@Override
	public void deductPoints() {
		throw EXCEPTION;
		
	}

	@Override
	public boolean luckyDraw() {
		throw EXCEPTION;
	}

	@Override
	public void giveOutPrize() {
		//礼物数量减一
		this.context.count--;
		System.out.println("成功领取到奖品!");
		
		//如果领取完奖品后还有奖品,则将当前状态设置为不可抽奖状态,否则设置为奖品领完状态
		if(this.context.count > 0) {
			this.context.currentState = Context.CANNOT_LUCKY_DRAW_STATE;
		}else {
			this.context.currentState = Context.PRIZE_EMPTY_STATE;
		}
		
	}

	

}


/**
 * 
 * @ClassName PrizeEmptyState
 * @Description 奖品领完状态
 * @author fuling
 * @date 2020年11月3日 下午3:58:40
 */
class PrizeEmptyState implements State{
	
	Context context;
	
	PrizeEmptyState(Context context){
		this.context = context;;
	}

	@Override
	public void deductPoints() {
		System.out.println("奖品已经领完了");
		
	}

	@Override
	public boolean luckyDraw() {
		System.out.println("奖品已经领完了");
		return false;
	}

	@Override
	public void giveOutPrize() {
		
		System.out.println("奖品已经领完了");
	}

	
	
	
}

用状态模式的方案遵守了开闭原则,如果此时我们需要添加一种新状态,则只需要实现状态接口即可,不需要修改客户端代码。

状态模式小结
  1. 主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
  2. 当一个对象的状态改变时,允许其改变行为,这个对象看起来像是改变了其类
  3. 该模式主要有两个角色:Context类为环境角色,用于维护state的实例;State为抽象状态角色(状态接口),定义一个接口封装特定的行为活动
  4. 状态模式将每个状态的行为封装到对应的一个类中,代码有很强的可读性
  5. 将容易产生问题的if/else语句删除了,方便维护。如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态
    ,不但会产生很多if/else语句,而且容易出错
  6. 符合开闭原则,容易增删状态
  7. 如果状态很多的话,会产生很多状态类,加大维护难度
  8. 当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值