状态模式(二十四)

相信自己,请一定要相信自己

上一章简单介绍了 解释器模式(二十三), 如果没有看过, 请观看上一章

一. 状态模式

引用 菜鸟教程里面 状态模式介绍: https://www.runoob.com/design-pattern/state-pattern.html

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

一.一 介绍

意图: 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

主要解决: 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

何时使用: 代码中包含大量与对象状态有关的条件语句。

如何解决: 将各种具体的状态类抽象出来。

关键代码: 通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。
而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。
也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。
状态模式和命令模式一样,也可以用于消除 if…else 等条件选择语句。

应用实例:
1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。
2、曾侯乙编钟中,‘钟是抽象接口’,'钟A’等是具体状态,'曾侯乙编钟’是具体环境(Context)。

优点:
1、封装了转换规则。
2、枚举可能的状态,在枚举状态之前需要确定状态种类。
3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:
1、状态模式的使用必然会增加系统类和对象的个数。
2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。

注意事项: 在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。


组成角色 具体 关系
抽象状态角色 (State) State 抽象状态角色,负责对象状态定义,并且封装环境角色以实现状态切换
具体状态角色 (ConcreteState) NoChouState,CanChouState 具体状态主要有两个职责:
一是处理本状态下的事情,二是从本状态如何过渡到其他状态
环境角色(Context) ActivityContext 定义客户端需要的接口,并且负责具体状态的切换

image-20230615200046901

二. 状态模式实例

二.一 抽象状态角色 State

public abstract class State {
    
    
    /**
      减少积分
     */
    public abstract  void deductScore();
    /**
     是否抽中奖品
     */
    public abstract  boolean raffle();
    /**
    获取奖品
     */
    public abstract  void sendPrize();

}

二.二 具体状态角色实现

有 不能抽奖, 可以抽奖, 获取礼品, 礼品发送完活动结束 四个状态

二.二.一 不能抽奖 NoChouState

@Slf4j
public class NoChouState extends State{
    
    
    private ActivityContext activityContext;

    public NoChouState(ActivityContext activityContext) {
    
    
        this.activityContext = activityContext;
    }

    @Override
    public void deductScore() {
    
    
       log.info(">>> 扣除积分成功");
       activityContext.setState(activityContext.getCanChouState());
    }

    @Override
    public boolean raffle() {
    
    
        log.info(">>> 扣了积分才能抽");
        return false;
    }

    @Override
    public void sendPrize() {
    
    
        log.info(">>> 未抽奖,不发放奖品");
    }
}

二.二.二 可以抽奖 CanChouState

@Slf4j
public class CanChouState extends State{
    
    
    private ActivityContext activityContext;

    public CanChouState (ActivityContext activityContext) {
    
    
        this.activityContext = activityContext;
    }

    @Override
    public void deductScore() {
    
    
       log.info(">>> 不能调用扣取积分方法");
    }

    @Override
    public boolean raffle() {
    
    

        // 生成一个随机数
        int randNum = new Random().nextInt(3);
        if (randNum == 0) {
    
    
            //改变状态
            activityContext.setState(activityContext.getSendState());
            return true;
        }else {
    
    
            activityContext.setState(activityContext.getNoChouState());
            return false;
        }
    }

    @Override
    public void sendPrize() {
    
    
        log.info(">>> 没有中奖,不发放奖品");
    }
}

二.二.三 抽中,获取奖品 SendState

@Slf4j
public class SendState extends State{
    
    
    private ActivityContext activityContext;

    public SendState(ActivityContext activityContext) {
    
    
        this.activityContext = activityContext;
    }

    @Override
    public void deductScore() {
    
    
       log.info(">>> 不扣除积分");
    }

    @Override
    public boolean raffle() {
    
    

        log.info("抽过奖了");
        return false;
    }

    @Override
    public void sendPrize() {
    
    
        if (activityContext.getCount() >0) {
    
    
            log.info("中奖了");
            activityContext.setCount(activityContext.getCount()-1);
            activityContext.setState(activityContext.getNoChouState());
        } else {
    
    
            log.info(">>> 抽奖活动结束了");
            activityContext.setState(activityContext.getSendOutState());
        }
    }
}

二.二.四 奖品发完了,活动结束 SendOutState

@Slf4j
public class SendOutState extends State{
    
    
    private ActivityContext activityContext;

    public SendOutState(ActivityContext activityContext) {
    
    
        this.activityContext = activityContext;
    }

    @Override
    public void deductScore() {
    
    
       log.info(">>> 奖品发送完了,请下次参加");
    }

    @Override
    public boolean raffle() {
    
    

        log.info(">>> 奖品发送完了,请下次参加");
        return false;
    }

    @Override
    public void sendPrize() {
    
    
        log.info(">>> 奖品发送完了,请下次参加");
    }
}

二.三 环境角色 ActivityContext

@Data
public class ActivityContext {
    
    
    private State state;
    private int count;
    // 构建这四个状态
    private State noChouState = new NoChouState(this);
    private State canChouState = new CanChouState(this);

    private State sendState = new SendState(this);
    private State sendOutState = new SendOutState(this);


    public ActivityContext(int count) {
    
    
        this.state = noChouState;
        this.count = count;
    }

    public void deductScore() {
    
    
       this.state.deductScore();
    }

    public void raffle() {
    
    
        if (this.state.raffle()){
    
    
            this.state.sendPrize();
        }
    }
}

二.四 客户端调用

10 个用户, 抽3个奖品

/**
     * 状态有: 不能抽奖,能抽奖
     *
     * 状态运转是:
     *
     * 不能抽奖--- > 能提奖---> 抽中了 (不能提奖 /活动结束了)
     */

    @Test
    public void oneTest() {
    
    
        ActivityContext activityContext = new ActivityContext(3);

        for (int i = 0; i < 10; i++) {
    
    
            log.info(">>> 第 {} 次抽奖", i + 1);
            activityContext.deductScore();
            // 抽奖
            activityContext.raffle();
        }
    }

image-20230615200627387


本章节的代码放置在 github 上:


https://github.com/yuejianli/DesignPattern/tree/develop/State


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

猜你喜欢

转载自blog.csdn.net/yjltx1234csdn/article/details/131235030