Java代码审计-设计模式-状态模式

Java设计模式-状态模式(Builder Pattern)

目录

  • 什么是状态模式
  • 状态模式的实现
  • JavaSE中状态模式的使用
  • Struts2状态模式的应用

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

一、什么状态模式

状态模式的定义为:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

看下状态模式的UML类图

image-20230325221029124

环境(Context)角色:也称上下文,通常用来定义客户端感兴趣的接口,同时维护一个来具体处理当前状态的对象示例。

抽象状态(State)角色:定义一个接口,用来封装与环境(Context)对象的一个特定的状态所对应的行为。

具体状态(ConcreteState)角色:每一个具体状态类都实现了一个跟环境(Context)相关的状态的具体处理。

状态模式只看定义比较难理解,我们来说下他的核心思想:

  • 各个状态是单独的类,状态直接可以相互切换(通过状态类中的方法进行切换)
  • 环境角色中持有所有的状态类,并且环境角色类维护一个当前状态,这个当前状态就是各个状态类来动态改变的,也就是说各个状态类中也持有环境角色对象

所以状态模式的重点就是:状态的切换状态的维护

二、状态模式的实现

我们通过一个抽奖活动的案例讲解下状态模式,先看下UML类图与核心类介绍

image-20230421170454280

环境(Context)角色:RaffleActivity

抽象状态(State)角色:State

具体状态(ConcreteState)角色:CanRaffleState、DispenseOutState、DispenseState、NoRaffleState

大家都玩过抽奖活动,比如超市中可以使用积分兑换抽奖活动,当所有的奖品都发放后抽奖活动就结束了。对于这个场景而已,环境角色就是抽奖活动,而是否可以抽奖就是状态。简单分析下就能得到上图中的类图关系。

RaffleActivity持有四个活动状态,每个活动状态中都有RaffleActivity对象,为的就是切换状态,比如有人中奖了,就要将RaffleActivity的状态变为DispenseState

接下来看下代码实现:

package org.state.version1;

import java.util.Random;

abstract class State{
    public RaffleActivity raffleActivity;
    public State(RaffleActivity raffleActivity){
        this.raffleActivity = raffleActivity;
    }
    // 扣除积分
    public abstract void deductMoney();
    // 抽奖动作
    public abstract boolean raffle();
    // 发放奖品
    public abstract void dispensePrize();
}

/**
 * 可以抽奖的状态类
 */
class CanRaffleState extends State{

    public CanRaffleState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("正在抽奖,无需扣除积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("正在抽奖");
        Random random = new Random();

        if (random.nextInt(10) == 1){
            System.out.println("恭喜您中奖了");
            // 修改活动状态为发放奖品状态
            super.raffleActivity.setState(super.raffleActivity.dispenseState);
            return true;
        }else {
            System.out.println("很遗憾没有中奖");
            super.raffleActivity.setState(super.raffleActivity.noRaffleState);
            return false;
        }
    }

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

/**
 * 奖品发放的状态类
 */
class DispenseState extends State{

    public DispenseState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("已经抽到奖了,不需要扣除积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("发放奖品不需要扣除积分");
        return false;
    }

    @Override
    public void dispensePrize() {
        if (super.raffleActivity.getCount() > 0) {
            // 设置发奖完成状态
            super.raffleActivity.setState(super.raffleActivity.dispenseOutState);
            super.raffleActivity.dispense();
        }else {
            System.out.println("奖品已经发放完毕");
        }

    }
}

/**
 * 奖品发放完毕的状态类
 */
class DispenseOutState extends State{

    public DispenseOutState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("奖品已经发放完毕,无需扣除积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("奖品已经发放完毕,不能抽奖");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("奖品已经发放完毕");
    }
}

/**
 * 不能抽奖的状态类
 */
class NoRaffleState extends State{

    public NoRaffleState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public void deductMoney() {
        System.out.println("扣除10个积分,可以抽奖了");
        super.raffleActivity.setState(super.raffleActivity.canRaffleState);
    }

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

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

class RaffleActivity{
    // 礼物数量
    private int count;

    private State state;
    public final State canRaffleState = new CanRaffleState(this);
    public final State dispenseOutState = new DispenseOutState(this);
    public final State dispenseState = new DispenseState(this);
    public final State noRaffleState = new NoRaffleState(this);

    public RaffleActivity(int count){
        this.setCount(count);
        setState(noRaffleState);
    }

    // 抽奖
    public boolean raffle(){
        // 开始抽奖
        if(state.raffle()){
            // 如果中奖了,则调用状态对应的发放礼品方法
            state.dispensePrize();
            return true;
        }
        return false;
    }
    // 扣除积分
    public void deductMoney(){
        this.state.deductMoney();
    }
    // 发放礼品
    public void dispense(){
        // 礼品减1
        this.count = this.count - 1;
        this.state.dispensePrize();
    }
    public int getCount() {
        return count;
    }

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

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

}


public class Test {
    public static void main(String[] args) {
        RaffleActivity raffleActivity = new RaffleActivity(1);
        for (int i = 0; i < 10; i++){
            System.out.println("开始第【" + i + "】次抽奖");
            // 扣除积分
            raffleActivity.deductMoney();
            // 开始抽奖,如果中奖就结束
            if (raffleActivity.raffle()) {return;}
        }
    }
}
// 运行结果
开始第【0】次抽奖
扣除10个积分,可以抽奖了
正在抽奖
很遗憾没有中奖
......
很遗憾没有中奖
开始第【7】次抽奖
扣除10个积分,可以抽奖了
正在抽奖
恭喜您中奖了
奖品已经发放完毕

代码很多,我来精简下

package org.state.version1;

import java.util.Random;

abstract class State{
    public RaffleActivity raffleActivity;
    public State(RaffleActivity raffleActivity){
        this.raffleActivity = raffleActivity;
    }
    // 扣除积分
    public abstract void deductMoney();
    // 抽奖动作
    public abstract boolean raffle();
    // 发放奖品
    public abstract void dispensePrize();
}

/**
 * 可以抽奖的状态类
 */
class CanRaffleState extends State{

    public CanRaffleState(RaffleActivity raffleActivity) {
        super(raffleActivity);
    }

    @Override
    public boolean raffle() {
        System.out.println("正在抽奖");
        Random random = new Random();

        if (random.nextInt(10) == 1){
            System.out.println("恭喜您中奖了");
            // 修改活动状态为发放奖品状态
            super.raffleActivity.setState(super.raffleActivity.dispenseState);
            return true;
        }else {
            System.out.println("很遗憾没有中奖");
            super.raffleActivity.setState(super.raffleActivity.noRaffleState);
            return false;
        }
    }
}

/**
 * 奖品发放的状态类
 */
class DispenseState extends State{

    @Override
    public void dispensePrize() {
        if (super.raffleActivity.getCount() > 0) {
            // 设置发奖完成状态
            super.raffleActivity.setState(super.raffleActivity.dispenseOutState);
            super.raffleActivity.dispense();
        }else {
            System.out.println("奖品已经发放完毕");
        }

    }
}

class RaffleActivity{
    // 礼物数量
    private int count;

    private State state;
    public final State canRaffleState = new CanRaffleState(this);
    public final State dispenseOutState = new DispenseOutState(this);
    public final State dispenseState = new DispenseState(this);
    public final State noRaffleState = new NoRaffleState(this);

    public RaffleActivity(int count){
        this.setCount(count);
        setState(noRaffleState);
    }

    // 抽奖
    public boolean raffle(){
        // 开始抽奖
        if(state.raffle()){
            // 如果中奖了,则调用状态对应的发放礼品方法
            state.dispensePrize();
            return true;
        }
        return false;
    }

}

// 另外两个装态没什么用,省略掉
public class Test {
    public static void main(String[] args) {
        RaffleActivity raffleActivity = new RaffleActivity(1);
        for (int i = 0; i < 10; i++){
            System.out.println("开始第【" + i + "】次抽奖");
            // 扣除积分
            raffleActivity.deductMoney();
            // 开始抽奖,如果中奖就结束
            if (raffleActivity.raffle()) {return;}
        }
    }
}

可以看到其实就是抽奖、发送奖品两个核心的点,生活中的抽奖无非也就是这样。比较核心的点就是抽奖的中奖逻辑以及中奖后的切换状态。每次抽奖都会调用raffle方法,当中奖后调用state.dispensePrize();此次的state已经被CanRaffleState类raffle方法变为super.raffleActivity.dispenseState(发放奖品状态)。状态的变化对于用户来说无感知,都是状态类内部动态修改。

使用状态模式的优点

  1. **简化应用逻辑控制:**状态模式使用单独的类来封装一个状态的处理,可以把负责逻辑控制的代码分散到单独的状态类中去,这样就把着眼点从执行状态提高到整个对象的状态,使得代码结构化和意图更清晰,从而简化应用的逻辑控制。
  2. **更好地分离状态和行为:**状态模式通过设置所有状态类的公共接口,把状态和状态对应的行为分离开,把所有与一个特定的状态相关的行为都放入一个对象中,使得应用程序在控制的时候,只需要关心状态的切换,而不用关心这个状态对应的真正处理。
  3. **更好的扩展性:**引入了状态处理的公共接口后,使得扩展新的状态变得非常容易,只需要新增加一个实现状态处理的公共接口的实现类,然后在进行状态维护的地方,设置状态变化到这个新的状态即可。
  4. **显式化进行状态转换:**状态模式为不同的状态引入独立的对象,使得状态的转换变得更加明确。而且状态对象可以保证上下文不会发生内部状态不一致的情况,因为上下文中只有一个变量来记录状态对象,只要为这一个变量赋值就可以了。

使用状态模式的缺点:

  1. 一个状态对应一个状态处理类,会使得程序引入太多的状态类,这样程序变得杂乱。

下图是IDEA自动生成的UML图,可以和我们画的UML图对比下

image-20230326112523317

三、JavaSE中状态模式的使用

暂时没发现,如果读者有发现,还请私信我添加,谢谢

四、Struts2状态模式的应用

暂时没发现,如果读者有发现,还请私信我添加,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MarginSelf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值