欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式——状态模式

程序员文章站 2022-06-17 17:08:48
...

在现实生活中,常常会出现这样的事例:一个人的情绪高兴时,会做出一些助人为乐的事情。情绪低落的时候,会做出一些伤天害理的事情。这里的情绪就是状态,对应做的事情就是行为。在软件开发中也是类似的,有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态会发生改变,从而使得其行为也随之发生改变。

一、基本介绍


【1】状态(State)模式的定义:对有状态的对象,把复杂的 “判断逻辑” 提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
【2】状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为问题。状态和行为是一一对应的,状态之间可以相互转换。
【3】当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。
【4】这种类型的设计模式属于行为型模式

二、状态模式的结构


状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。状态模式包含以下主要角色:
【1】环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
【2】抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
【3】具体状态(Concrete State)角色:实现抽象状态所对应的行为。
 设计模式——状态模式

三、状态模式的应用案例


使用状态模式解决 APP 抽奖问题:根据如下流程中的状态,完成具体的业务操作。
设计模式——状态模式

【1】应用的结构类图:精华在RaffleActive(上下文类)和状态的子类中。两者之间相互组合,减少复杂的逻辑判断。
设计模式——状态模式

【2】State 接口的实现如下:

/*
 *  状态对应的抽象行为
 */
public interface State {
	//扣除积分
	public void deduceMoney();
	//是否中奖
	public boolean raffle();
	//发放奖品
	public void dispensePrize();
}

【3】State 子类一:扣除积分类 NoRaffleState 的实现如下,扣除成功后,将 state 设置为抽奖状态。

public class NoRaffleState implements State{
	
	//初始化时传入活动引用,扣除积分后改变其状态
	RaffleActivity active;
	
	//构造器
	public NoRaffleState(RaffleActivity active) {
		this.active = active;
	}

	// 当前状态可以扣分,扣分后修改状态
	@Override
	public void deduceMoney() {
		System.out.println("扣除5个积分");
		active.setState(active.getCanRaffeState());
		
	}
	@Override
	public boolean raffle() {
		System.out.println("抽了积分才能抽奖");
		return false;
	}

	@Override
	public void dispensePrize() {
		System.out.println("不能方法奖品");
	}

}

【4】 State 子类一:抽奖状态类 CanRaffeState,如果抽中设置状态为抽中状态,未抽中时设置状态为扣积分状态。

public class CanRaffeState implements State{
	RaffleActivity active;
	
	//构造器
	public CanRaffeState(RaffleActivity active) {
		this.active = active;
	}

	//扣积分
	@Override
	public void deduceMoney() {
		System.out.println("已扣除积分");
	}

	@Override
	public boolean raffle() {
		System.out.println("正在抽奖,请稍等");
		int num = new Random().nextInt(5);
		//20% 的中奖机会,中了则返回true
		if(num == 0) {
			active.setState(active.getDispenseState());
			return true;
			
		}else {
			System.out.println("很遗憾没有抽中奖品");
			active.setState(active.getNoRaffleState());
			return false;
		}
	}

	@Override
	public void dispensePrize() {
		System.out.println("抽奖中,不能发送奖品");
	}

}

【5】 State 子类一:抽中状态类 DispenseState,如果礼物未送完,则发送礼物,并设置状态为扣分状态。否则设置为礼物以发放完,且活动结束状态。

public class DispenseState implements State{
	RaffleActivity active;
	
	//构造器
	public DispenseState(RaffleActivity active) {
		this.active = active;
	}

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

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

	@Override
	public void dispensePrize() {
		if(active.getCount()>0) {
			System.out.println("恭喜中奖了,奖品已发货");
			active.setState(active.getNoRaffleState());
		}else {
			System.out.println("很遗憾,奖品已发完");
			active.setState(active.getDispenseOutState());
		}
	}
}

【6】 State 子类一:礼物发放完,却活动结束状态类 DispenseOutState

public class DispenseOutState implements State{
	
	@Override
	public void deduceMoney() {
		System.out.println("活动结束");
	}

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

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

}

【7】上下文类 RaffleActivity,主要存储用户的状态和礼物的总记录数等重要信息。并组合所有的状态子类,传入自身对象。

public class RaffleActivity {
	//状态
	private State state;
	//奖品记录数
	private int count;
	
	//构造器,初始化上述两个属性
	public RaffleActivity(int count) {
		//客户端创建的时候,说明开始是开始抽奖状态。
		state = noRaffleState;
		this.count = count;
	}
	
	NoRaffleState noRaffleState = new NoRaffleState(this);
	CanRaffeState canRaffeState = new CanRaffeState(this);
	DispenseState dispenseState = new DispenseState(this);
	DispenseOutState dispenseOutState = new DispenseOutState();
	
	// 当前状态可以扣分,扣分后修改状态
	public void deduceMoney() {
		state.deduceMoney();
	}
	public void raffle() {
		if(state.raffle()) {
			state.dispensePrize();
		} 
	}
	
	//需要注意,我们的数量应该是递减的
	public int getCount() {
		int countNum = count;
		count--;
		return countNum;
	}

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

	public State getState() {
		return state;
	}
	
	public void setState(State state) {
		this.state = state;
	}
	
	public NoRaffleState getNoRaffleState() {
		return noRaffleState;
	}

	public CanRaffeState getCanRaffeState() {
		return canRaffeState;
	}

	public DispenseState getDispenseState() {
		return dispenseState;
	}

	public DispenseOutState getDispenseOutState() {
		return dispenseOutState;
	}
}

【8】客户端端调用类 Client,只需要调用上下文类,便可实现客户端的需求。

public class Client {
	
	public static void main(String[] args) {
		//为了演示方便,就定义只有一个奖品
		RaffleActivity activity = new RaffleActivity(1);
		for(int i=0;i<30;i++) {
			System.out.println("======第"+i+"次,抽取奖品========");
			//扣积分
			activity.deduceMoney();
			//抽奖
			activity.raffle();
		}
	}
}

四、状态模式的特点


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

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

----如果喜欢,点个  红心♡  支持以下,谢谢----

相关标签: 状态模式