文章目录
在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。
当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。如人的情绪有高兴的时候和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。
对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else
语句来做状态判断,再进行不同情况的处理。但当对象的状态很多时,程序会变得很复杂。而且增加新的状态要添加新的 if-else
语句,这违背了“开闭原则”,不利于程序的扩展。
以上问题如果采用 状态模式
就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,放到一系列的状态类当中,这样可以把原来复杂的逻辑判断简单化。
一、定义
状态的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
二、特点
状态模式是一种对象行为型模。
2.1、优点:
- 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
- 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
2.2、缺点:
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
三、角色和UML类图
状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。
3.1、主要角色:
Context
(环境):也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。State
(抽象状态):定义一个接口,用以封装环境对象中的特定状态所对应的行为。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();
}
}
}