设计模式之状态模式(State Pattern)

在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。

当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。如人的情绪有高兴的时候和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。

对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else 语句来做状态判断,再进行不同情况的处理。但当对象的状态很多时,程序会变得很复杂。而且增加新的状态要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

以上问题如果采用 状态模式 就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,放到一系列的状态类当中,这样可以把原来复杂的逻辑判断简单化。

一、定义

状态的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

二、特点

状态模式是一种对象行为型模。

2.1、优点:

  1. 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  2. 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  3. 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

2.2、缺点:

  1. 状态模式的使用必然会增加系统的类与对象的个数。
  2. 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

三、角色和UML类图

状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。

3.1、主要角色:

  1. Context (环境):也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  2. State (抽象状态):定义一个接口,用以封装环境对象中的特定状态所对应的行为。
  3. ConcreteState (具体状态):实现抽象状态所对应的行为。

3.2、UML类图

在这里插入图片描述

四、示例

需求:请编写程序完成APP抽奖活动 具体要求如下:

1)假如每参加一次这个活动要先扣除用户50积分,再抽奖,中奖概率是10%;

2)奖品数量固定,抽完就不能抽奖;

3)活动有四个状态: 可以抽奖、不能抽奖、发放奖品 和 奖品领完;

4.1、代码:

定义4种状态:

/**  不能抽奖状态 */
State noRafflleState = new StateForNoRaffle(this);
/**  可以抽奖的状态*/
State canRaffleState = new StateForCanRaffle(this);

/**  发放奖品的状态 */
State dispenseState = new StateForDispense(this);
/**  奖品发放完毕,请下次再参加  */
State dispensOutState = new StateForDispenseOut(this);
4.1.1、State:状态
/**
 * 状态
 */
public abstract class State {

	/**
	 * 扣除积分 - 50
	 */
	public abstract void deductScore();

	/**
	 * 是否抽中奖品
	 * 
	 * @return
	 */
	public abstract boolean raffle();

	/**
	 * 发放奖品
	 */
	public abstract void dispensePrize();

}
4.1.2、StateForNoRaffle:NoRaffle(不能抽奖状态)
/**
 * NoRaffle(不能抽奖状态)
 */
public class StateForNoRaffle extends State {

	// 初始化时传入活动引用,扣除积分后改变其状态
	RaffleActivity activity;

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

	/**
	 *  当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态
	 */
	@Override
	public void deductScore() {
		System.out.println("扣除50积分成功,您可以抽奖了");
		activity.setState(activity.getCanRaffleState());
	}

	/**
	 *  当前状态不能抽奖
	 */
	@Override
	public boolean raffle() {
		System.out.println("扣了积分才能抽奖喔!");
		return false;
	}

	/**
	 * 当前状态不能发奖品
	 */
	@Override
	public void dispensePrize() {
		System.out.println("不能发放奖品");
	}
}
4.1.3、StateForCanRaffle:CanRaffle(可以抽奖)
import java.util.Random;

/**
 * CanRaffle(可以抽奖)
 *
 */
public class StateForCanRaffle extends State {

	RaffleActivity activity;

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

	/**
	 * 已经扣除了积分,不能再扣
	 */
	@Override
	public void deductScore() {
		System.out.println("已经扣取过了积分");
	}

	/**
	 *  可以抽奖, 抽完奖后,根据实际情况,改成新的状态
	 */
	@Override
	public boolean raffle() {
		System.out.println("正在抽奖,请稍等!");
		Random r = new Random();
		int num = r.nextInt(10);
		// 10%中奖机会
		if (num == 0) {
			// 改变活动状态为发放奖品 context
			activity.setState(activity.getDispenseState());
			return true;
		} else {
			System.out.println("很遗憾没有抽中奖品!");
			// 改变状态为不能抽奖
			activity.setState(activity.getNoRafflleState());
			return false;
		}
	}

	/**
	 * 不能发放奖品
	 */
	@Override
	public void dispensePrize() {
		System.out.println("没中奖,不能发放奖品");
	}
}
4.1.4、StateForDispense:Dispense(发放奖品)
/**
 * Dispense(发放奖品)
 */
public class StateForDispense extends State {

	// 初始化时传入活动引用,发放奖品后改变其状态
	RaffleActivity activity;

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

	//

	@Override
	public void deductScore() {
		System.out.println("不能扣除积分");
	}

	@Override
	public boolean raffle() {
		System.out.println("不能抽奖");
		return false;
	}

	// 发放奖品
	@Override
	public void dispensePrize() {
		if (activity.getCount() > 0) {
			System.out.println("恭喜中奖了");
			
			// 改变状态为不能抽奖,重置抽奖程序
			activity.setState(activity.getNoRafflleState());
		} else {
			
			System.out.println("很遗憾,奖品发送完了");
			
			/**
			 * 因为奖品数量为0,因此将活动状态改为,请下次再参加。
			 */
			// 后面我们就不可以抽奖
			activity.setState(activity.getDispensOutState());
			// System.out.println("抽奖活动结束");
			// System.exit(0);
		}

	}
}

4.1.5、StateForDispenseOut:DispenseOut(奖品发放完了,请下次再参加)
/**
 * DispenseOut(奖品发放完了,请下次再参加)
 * 
 * <pre>
 * 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束
 * 
 * </pre>
 */
public class StateForDispenseOut extends State {

	// 初始化时传入活动引用
	RaffleActivity activity;

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

	@Override
	public void deductScore() {
		System.out.println("奖品发送完了,请下次再参加");
	}

	@Override
	public boolean raffle() {
		System.out.println("奖品发送完了,请下次再参加");
		return false;
	}

	@Override
	public void dispensePrize() {
		System.out.println("奖品发送完了,请下次再参加");
	}
}
4.1.6、RaffleActivity:Context(环境):抽奖活动
/**
 * Context(环境):抽奖活动
 */
public class RaffleActivity {

	// state 表示活动当前的状态,是变化
	State state = null;
	// 奖品数量
	int count = 0;

	/**
	 * 四个属性,表示四种状态
	 */
	/**  不能抽奖状态 */
	State noRafflleState = new StateForNoRaffle(this);
	
	/**  可以抽奖的状态*/
	State canRaffleState = new StateForCanRaffle(this);

	
	/**  发放奖品的状态 */
	State dispenseState = new StateForDispense(this);
	
	/**  奖品发放完毕,请下次再参加  */
	State dispensOutState = new StateForDispenseOut(this);

	/**
	 * 构造器
	 */
	// 1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
	// 2. 初始化奖品的数量
	public RaffleActivity(int count) {
		this.state = getNoRafflleState();
		this.count = count;
	}

	/**
	 * 第1步,扣分, 调用当前状态的 deductMoney
	 */
	public void debuctMoney() {
		state.deductScore();
	}

	/**
	 * 第2步,抽奖
	 */
	public void raffle() {
		// 第3步,判断是否抽奖成功
		if (state.raffle()) {
			// 第4步,领取奖品
			state.dispensePrize();
		}
	}

	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	// 这里请大家注意,每领取一次奖品,count--
	public int getCount() {
		int curCount = count;
		count--;
		return curCount;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public State getNoRafflleState() {
		return noRafflleState;
	}

	public void setNoRafflleState(State noRafflleState) {
		this.noRafflleState = noRafflleState;
	}

	public State getCanRaffleState() {
		return canRaffleState;
	}

	public void setCanRaffleState(State canRaffleState) {
		this.canRaffleState = canRaffleState;
	}

	public State getDispenseState() {
		return dispenseState;
	}

	public void setDispenseState(State dispenseState) {
		this.dispenseState = dispenseState;
	}

	public State getDispensOutState() {
		return dispensOutState;
	}

	public void setDispensOutState(State dispensOutState) {
		this.dispensOutState = dispensOutState;
	}
}
4.1.7、Client
/**
 * 状态模式的测试类
 */
public class Client {

	public static void main(String[] args) {
		// 创建活动对象,奖品有1个奖品
		RaffleActivity activity = new RaffleActivity(1);

		/**
		 * 我们连续抽300次奖
		 */
		for (int i = 0; i < 30; i++) {
			System.out.println("--------第" + (i + 1) + "次抽奖----------");

			// 第1步,点击扣除积分
			activity.debuctMoney();

			// 第2步,抽奖
			activity.raffle();
		}
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值