7.5 状态模式(State Pattern)

一. 定义

在软件开发过程中,应用程序中的 部分对象 可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把 影响对象行为的一个或多个动态变换的属性 称为状态, 当有状态的对象与外部事件产生互动时,其内部状态就会发生改变,从而使其行为发生改变;

1. 状态模式主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题,状态和行为是一一对应的,状态之间可相互转换;

2. 属于行为型模式, 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来向改变了其类;

二. 特点

1. 优点

    1) 代码结构清晰有很强的可读性,状态模式将每个状态的行为封装到对应的一个类中,符合单一职责原则;

     2) 将容易产生问题的if-else语句删除了,不用把所有状态行为都放到一个类中,调用时还要判断状态;

     3) 将状态转换显示化,减少对象间的相互依赖,将不同的状态引入独立的对象中使状态转换更加明确,减少对象间的依赖;

2. 缺点

    1) 会产生很多类,每个状态都要对应一个类,当状态过多时会产生很多类或兑现个数;

    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积分成功,您可以抽奖了
正在抽奖,请稍等!
恭喜中奖了!
奖品发送完毕,抽奖活动结束

六. 状态模式 与 责任链模式 的区别

  1. 状态模式与责任链模式都能消除 if-else分支过多的问题,在状态模式中的状态理解为责任时,两种模式都可使用;

  2. 从定义上来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象的改变;

  1. 两者最大的区别是:

状态模式中每个状态类中既包含了符合条件的处理方法,也在不符合条件时指明下一个状态类,然后执行该状态类的处理方法,

而责任链式中,每个处理类的处理方法中只有处理当前状态的方法,若不能处理时下一处理类由客户端指定;

  1. 综上所述: 状态模式的判断流程是底层模块中定义好的, 责任链模式的处理流程是让用户自己设置的;

七.状态模式 与 策略模式 的区别

状态模式 与 策略模式 的UML 类图几乎完全一样,但两者的应用场景是不一样的:

策略模式的多种算法行为择其一就能满足,彼此间是独立的,用户可自行更换策略算法,

而状态模式的各个状态间存在相互关系,彼此间在一定条件下存在自动切换状态的效果,用户无法指定状态,只能设置初始状态;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值