一. 定义
在软件开发过程中,应用程序中的 部分对象 可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把 影响对象行为的一个或多个动态变换的属性 称为状态, 当有状态的对象与外部事件产生互动时,其内部状态就会发生改变,从而使其行为发生改变;
1. 状态模式主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题,状态和行为是一一对应的,状态之间可相互转换;
2. 属于行为型模式, 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来向改变了其类;
二. 特点
1. 优点
1) 代码结构清晰有很强的可读性,状态模式将每个状态的行为封装到对应的一个类中,符合单一职责原则;
2) 将容易产生问题的if-else语句删除了,不用把所有状态行为都放到一个类中,调用时还要判断状态;
3) 将状态转换显示化,减少对象间的相互依赖,将不同的状态引入独立的对象中使状态转换更加明确,减少对象间的依赖;
2. 缺点
1) 会产生很多类,每个状态都要对应一个类,当状态过多时会产生很多类或兑现个数;
2) 状态模式的结构与实现较为复杂,如果使用不当会导致程序结构和代码的混乱;
三. 应用场景
-
当一个对象的行为取决于它的状态,且它必须在运行时根据状态改变它的行为时,就可考虑使用状态模式;
-
一个操作中含有庞大的分支结构,且这些分支决定于对象的状态时;
四. 模式的结构
把多判断语句中每种判断状态封装成一个类,各状态类均有一个方法,用以对当前状态做出处理,并能指明不能处理时的下一个状态类;
原理结构图
模式角色职责
1)Context(环境角色): 也称上下文,它定义了客户端需要的接口,内部维护一个State实例定义当前状态,并负责具体状态的切换;
2)State(抽象状态角色): 定义一个接口,用于封装与Context对象的特定状态所对应的行为,可以是一个或多个行为;
3)ConcreteState(具体状态角色): 每个子类实现抽象状态所对应的行为,且在需要的情况下进行状态切换;
五. 模式的实现
定义一个状态接口,每个状态都实现它,接口有扣除积分方法,抽奖方法,发放奖品方法
1.实例结构图
2.相关代码实现
object StateClient {
@JvmStatic
fun main(args: Array<String>) {
//创建活动对象,奖品池有2个奖品
val activity = RaffleActivity(2)
//我们连抽三次奖
for (i in 0..29){
println("--------------第 ${i + 1} 次抽奖--------------")
//参加抽奖,第一步点击扣除积分
activity.deductMoney();
//第二步抽奖
activity.raffle()
if (activity.getState() is DispenseOutState) return
}
}
}
/**
* 抽奖活动 环境类
*/
class RaffleActivity(var count:Int) {
private var state: State //表示当前的状态, 是变化的
//四个属性, 表示四种状态
private var noRaffleState=NoRaffleState(this)
private var canRaffleState=CanRaffleState(this)
private var dispenseState=DispenseState(this)
private var dispenseOutState=DispenseOutState(this)
init {
state=getNoRaffleState() //初始化当前状态为不能抽奖状态
}
//扣除积分
fun deductMoney(){
state.deductMoney()
}
// 奖励奖品
fun raffle(){
//如果当前的状态是抽奖成功
if (state.raffle()) {
//领取奖品
state.dispensePrize()
}
}
fun setState(state:State){
this.state=state
}
fun getState():State{
return state
}
fun getNoRaffleState(): State{
return noRaffleState
}
fun getCanRaffleState(): State{
return canRaffleState
}
fun getDispenseState(): State{
return dispenseState
}
fun getDispenseOutState(): State{
return dispenseOutState
}
fun getCurCount():Int{
return count
}
fun updateCurCount() {
var curCount =count
curCount--
count =curCount
}
}
/**
* 状态接口
*/
interface State {
fun deductMoney() //扣除积分
fun raffle(): Boolean // 是否抽中奖品
fun dispensePrize()
}
abstract class State {
override fun deductMoney() {} //扣除积分
override fun raffle(): Boolean { // 是否抽中奖品
return false
}
override fun dispensePrize() {}
}
/**
* 不能抽奖
* activity 初始化时传入活动引用,扣除积分后改变其状态
*/
class NoRaffleState(val activity:RaffleActivity) : AbstractState(){
/**
* 当前状态可扣分,扣除后,将状态设置成可抽奖状态;
*/
override fun deductMoney() {
println("扣除50积分成功,您可以抽奖了")
activity.setState(activity.getCanRaffleState())
}
/**
* 当前状态不能抽奖
*/
override fun raffle(): Boolean {
println("扣了积分才能抽奖哦")
return false
}
/**
* 当前状态不能发奖品
*/
override fun dispensePrize() {
println("当前状态不能发奖品")
}
}
/**
* 可以抽奖状态
*/
class CanRaffleState(val activity: RaffleActivity) : AbstractState(){
//已经扣除积分,不能再扣
override fun deductMoney() {
println("已经扣取过了积分")
}
/**
* 可以抽奖,抽完奖后,根据新的情况改成新的状态
*/
override fun raffle(): Boolean {
println("正在抽奖,请稍等!")
val r = Random
val num=r.nextInt(10)
//10%中奖机会
return if (num == 1) {
//改变活动状态为发放奖品
activity.setState(activity.getDispenseState())
true
} else {
println("很遗憾没有抽中奖品! ")
//改变状态为不能抽奖
activity.setState(activity.getNoRaffleState())
false
}
}
/**
* 当前状态不能发奖品
*/
override fun dispensePrize() {
println("没中奖,不能发放奖品")
}
}
/**
* 发放奖品的状态
*/
class DispenseState(val activity: RaffleActivity) : AbstractState() {
/**
* 已经扣除积分,不能再扣
*/
override fun deductMoney() {
println("已经扣取过了积分")
}
/**
* 当前状态不能抽奖
*/
override fun raffle(): Boolean {
println("不能抽奖")
return false
}
/**
* 发放奖品
*/
override fun dispensePrize() {
if (activity.getCurCount() > 0) {
println("恭喜中奖了!")
activity.setState(activity.getNoRaffleState())
activity.updateCurCount()
if (activity.getCurCount() == 0) {
println("奖品发送完毕,抽奖活动结束")
//改变状态为奖品发送完毕,抽奖活动结束
activity.setState(activity.getDispenseOutState())
}
}
}
}
/**
* 奖品发放完毕状态
* 说明,当我们activity 改变成 DispenseOutState,抽奖活动结束
*/
class DispenseOutState(var activity: RaffleActivity) : AbstractState()
程序运行结果
--------------第 1 次抽奖--------------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------------第 2 次抽奖--------------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------------第 3 次抽奖--------------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
恭喜中奖了!
--------------第 4 次抽奖--------------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
很遗憾没有抽中奖品!
--------------第 5 次抽奖--------------
扣除50积分成功,您可以抽奖了
正在抽奖,请稍等!
恭喜中奖了!
奖品发送完毕,抽奖活动结束
六. 状态模式 与 责任链模式 的区别
-
状态模式与责任链模式都能消除 if-else分支过多的问题,在状态模式中的状态理解为责任时,两种模式都可使用;
-
从定义上来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象的改变;
-
两者最大的区别是:
状态模式中每个状态类中既包含了符合条件的处理方法,也在不符合条件时指明下一个状态类,然后执行该状态类的处理方法,
而责任链式中,每个处理类的处理方法中只有处理当前状态的方法,若不能处理时下一处理类由客户端指定;
-
综上所述: 状态模式的判断流程是底层模块中定义好的, 责任链模式的处理流程是让用户自己设置的;
七.状态模式 与 策略模式 的区别
状态模式 与 策略模式 的UML 类图几乎完全一样,但两者的应用场景是不一样的:
策略模式的多种算法行为择其一就能满足,彼此间是独立的,用户可自行更换策略算法,
而状态模式的各个状态间存在相互关系,彼此间在一定条件下存在自动切换状态的效果,用户无法指定状态,只能设置初始状态;