23种设计模式之状态模式

23种设计模式之状态模式

参考资料

下文如有错漏之处,敬请指正

一、简介

定义

允许一个对象在其内部状态发生改变时改变其行为。

特点

  • 状态模式是一种行为型模式。
  • 状态模式把一个对象因受环境改变而改变的行为包装在不同的状态对象里。

通用类图

在这里插入图片描述

状态模式的主要角色:

  • Context

    环境角色

    定义客户端需要的接口,并且负责具体状态的切换。

  • State

    抽象状态角色

    接口或抽象类,负责对象状态的定义,并且封装环境角色以实现状态切换。

  • ConcreteState

    具体状态角色

    实现抽象状态角色所对应的行为,负责本状态的行为管理以及本状态如何过渡到其他状态。

优点

  • 结构清晰

    避免了过多的switch…case或者if…else语句的使用,避免了程序的复杂性,提高系统的可维护性。

  • 遵循设计原则

    很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,你只修改一个子类就可以了。

  • 封装性非常好

    这也是状态模式的基本要求,状态和其行为的变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换,减少对象间的相互依赖。

缺点

  • 如果子类太多,会类膨胀;且如果状态模式的结构与实现都较为复杂,使用不当会导致程序结构和代码的混乱。其实有很多方式可以解决这个状态问题,如在数据库中建立一个状态表,然后根据状态执行相应的操作。

应用场景

  • 行为随状态改变而改变的场景

    当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。如域名备案(上传材料——腾讯云审核——提交管局——管局审核)

  • 条件、分支判断语句的替代者

    在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理。

二、状态模式

需求:

现在要使用某个APP进行抽奖,抽奖状态有:抽奖验证、正在抽奖、奖品领取、抽奖结束;使用状态模式模拟这一功能。

Context:

package state;

// 抽奖活动
public class Context {


	//  定义状态
	public static State raffleCheckState = new RaffleCheckState();//  抽奖验证状态
	public static State raffleDoingState = new RaffleDoingState();//  正在抽奖状态
	public static State grantAwardsState = new GrantAwardsState();//  领取奖品状态
	public static State raffleEndState = new RaffleEndState();    //  抽奖结束状态


	//  当前状态
	private State state;

	//  奖品的数量
	private int count;


	//  初始化奖品数量
	public Context(int count) {
		this.count = count;
	}


	//  设置当前状态
	public void setState(State state) {
		this.state = state;
		//  切换状态
		this.state.setContext(this);
	}


	public int getCount() {
		return count;
	}


	public void deCount() {
		--this.count;
	}

	//  ===========委托行为===========
	//  检查奖品数量行为
	public void checkAwardCount() {
		this.state.checkAwardCount();
	}

	//  抽奖行为
	public void raffle() {
		this.state.raffle();
	}

	//  领取奖品行为
	public void grantAwards() {
		this.state.grantAwards();
	}


}

State:

package state;

public abstract class State {

	//  定义一个环境角色,提供子类访问
	protected Context context;

	//  设置环境角色
	public void setContext(Context context) {
		this.context = context;
	}

	//  ===========行为===========
	//  检查奖品数量行为
	public abstract void checkAwardCount();

	//  抽奖行为
	public abstract void raffle();

	//  发放奖品行为
	public abstract void grantAwards();
}

ConcreteState:

package state;

//  抽奖验证状态
public class RaffleCheckState extends State {


	@Override
	public void checkAwardCount() {
		if (super.context.getCount() == 0) {
			//  没有奖品了,设置状态变为抽奖结束状态
			super.context.setState(Context.raffleEndState);
		} else {
			//  有奖品,设置状态变为正在抽奖状态
			super.context.setState(Context.raffleDoingState);
		}
		//	过渡到检查奖品数量行为
		super.context.raffle();

	}

	@Override
	public void raffle() {
		this.checkAwardCount();
	}

	@Override
	public void grantAwards() {
	}
}

package state;

import java.util.Random;

// 正在抽奖状态
public class RaffleDoingState extends State {

	@Override
	public void checkAwardCount() {
	}

	@Override
	public void raffle() {
		System.out.println("正在抽奖中……");
		Random random = new Random();
		//  随机抽奖编号
		int num = random.nextInt(10);
		//  默认中奖编号为7
		if (num == 7) {
			//  抽中了奖品,设置状态为领取奖品状态
			super.context.setState(Context.grantAwardsState);
			//  过渡到领取奖品行为
			super.context.grantAwards();
		} else {
			System.out.println("很遗憾你没有抽中奖品");
			//  设置状态为抽奖验证状态
			super.context.setState(Context.raffleCheckState);
		}
	}

	@Override
	public void grantAwards() {
	}
}

package state;

//  奖品领取状态
public class GrantAwardsState extends State {


	@Override
	public void checkAwardCount() {
	}

	@Override
	public void raffle() {
	}

	@Override
	public void grantAwards() {
		System.out.println("==恭喜你中奖了!请领取你的奖品==");
		//  将奖品数量-1
		super.context.deCount();
		//  设置状态变为状态
		super.context.setState(Context.raffleCheckState);

	}
}

package state;

//  抽奖结束状态
public class RaffleEndState extends State {


	@Override
	public void checkAwardCount() {
	}

	@Override
	public void raffle() {
		System.out.println("奖品已发放完毕,请期待下次的抽奖活动!");
		//  将状态变为抽奖结束状态
		System.out.println("============================================");
	}

	@Override
	public void grantAwards() {
	}
}

Client:

package state;

public class Client {
	public static void main(String[] args) {
		//  创建抽奖活动,共有3个奖品
		Context context = new Context(3);
		//  初始化状态
		context.setState(new RaffleCheckState());

		//  模拟用户抽奖100次
		for (int i = 0; i < 100; i++) {
			System.out.println("第" + (i + 1) + "次抽奖");
			//  模拟用户点击抽奖
			context.raffle();
		}
		/**
		 * 输出结果:
		 * 第1次抽奖
		 * 正在抽奖中……
		 * 很遗憾你没有抽中奖品
		 * 第2次抽奖
		 * 正在抽奖中……
		 * 很遗憾你没有抽中奖品
		 * 第3次抽奖
		 * 正在抽奖中……
		 * 很遗憾你没有抽中奖品
		 * --------------------此处省略-----------------
		 * 第15次抽奖
		 * 正在抽奖中……
		 * ==恭喜你中奖了!请领取你的奖品==
		 * --------------------此处省略-----------------
		 * 第22次抽奖
		 * 正在抽奖中……
		 * ==恭喜你中奖了!请领取你的奖品==
		 * --------------------此处省略-----------------
		 * 第28次抽奖
		 * 正在抽奖中……
		 * ==恭喜你中奖了!请领取你的奖品==
		 * 第29次抽奖
		 * 奖品已发放完毕,请期待下次的抽奖活动!
		 * ============================================
		 * --------------------此处省略-----------------
		 * 第100次抽奖
		 * 奖品已发放完毕,请期待下次的抽奖活动!
		 * ============================================
		 */
	}
}

当需要新增一个状态时,只需要增加一个子类(继承State),在Context环境中添加对应的状态即可。

三、总结

状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个,不然就会导致类膨胀,不易于维护。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介: 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 本课程内容定位学习设计原则,学习设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,我们要考虑人力、时间、成本、质量,不是刻意追求完美,要在适当的场景遵循设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。本章将详细介绍开闭原则(OCP)、依赖倒置原则(DIP)、单一职责原则(SRP)、接口隔离原则(ISP)、迪米特法则(LoD)、里氏替换原则(LSP)、合成复用原则(CARP)的具体内容。 为什么需要学习这门课程? 你在日常的开发中,会不会也遇到过同样的问题。系统出现问题,不知道问题究竟出在什么位置;当遇到产品需求,总是对代码缝缝补补,不能很快的去解决。而且平时工作中,总喜欢把代码堆在一起,出现问题时,不知道如何下手,工作效率很低,而且自己的能力也得不到提升。而这些都源于一个问题,那就是软件设计没做好。这门课能帮助你很好的认识设计模式,让你的能力得到提升。课程大纲: 为了让大家快速系统了解设计模式知识全貌,我为您总结了思维导图,帮您梳理学习重点,建议收藏!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值