设计模式 -- 状态模式(State Pattern)

1 问题引出

APP 抽奖活动问题

请编写程序完成 APP 抽奖活动 具体要求如下:

  1. 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10%

  2. 奖品数量固定,抽完就不能抽奖

  3. 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完‘

2 基本介绍

  1. 状态模式(State Pattern)是一种行为设计模式。

  2. 它允许对象在内部状态改变时改变其行为,使得对象在不同的状态下表现出不同的行为。状态和行为是一一对应的,状态之间可以相互转换。

  3. 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

3 原理结构图

3.1 类图

3.2 说明

  1. 上下文(Context):Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态

  2. 状态抽象类(State):State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为

  3. 具体状态类(Concrete State):实现状态抽象类,负责处理与特定状态相关的行为。具体状态类通常会维护一个对上下文对象的引用,以便根据不同的条件切换到不同的状态。

3.3 作用与原理

状态封装与变化:状态模式通过将每种状态的相关行为封装在不同的状态类中,并在上下文中引用当前状态对象,实现了状态的封装和变化。当对象的内部状态改变时,只需更改其关联的状态对象即可改变其行为。

避免条件语句:通过多态机制,状态模式避免了在上下文中使用条件语句来检查和处理多个状态的情况,从而简化了状态管理和代码维护。

4 应用实例

4.1 类图

4.2 代码实现

CanRaffleState

/**
 * 可以抽奖的状态
 */
public class CanRaffleState extends State {
​
    RaffleActivity activity;
​
    public CanRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }
​
    //  已经扣除了积分,不能再扣@Override
    public void deductMoney() {
        System.out.println("已经扣取过了积分");
    }
​
    //  可以抽奖, 抽完奖后,根据实际情况,改成新的状态
    @Override
    public boolean raffle() {
        System.out.println("正在抽奖,请稍等!");
        Random r = new Random();
        int num = r.nextInt(10);
        //  10%中奖机会
        if (num == 0) {
            //  改变活动状态为发放奖品 context
            activity.setState(activity.getDispenseState());
            return true;
        } else
​
        {
            System.out.println("很遗憾没有抽中奖品!");
            //  改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
            return false;
        }
    }
​
    //  不能发放奖品
    @Override
    public void dispensePrize() {
        System.out.println("没中奖,不能发放奖品");
    }
}

ClientTest

public class ClientTest {
​
    public static void main(String[] args) {
        //  TODO Auto-generated method stub
        //  创建活动对象,奖品有 1 个奖品
        RaffleActivity activity = new RaffleActivity(1);
​
        //  我们连续抽 300 次奖
        for (int i = 0; i < 30; i++) {
            System.out.println("--------第" + (i + 1) + "次抽奖 ");
            //  参加抽奖,第一步点击扣除积分
            activity.debuctMoney();
​
            //  第二步抽奖
            activity.raffle();
        }
    }
​
}

DispenseOutState

/**
 * 奖品发放完毕状态
 * 说明,当我们 activity 改变成 DispenseOutState, 抽奖活动结束
 * 
 * @author Administrator
 *
 */
public class DispenseOutState extends State {
​
    //  初始化时传入活动引用
    RaffleActivity activity;
​
    public DispenseOutState(RaffleActivity activity) {
        this.activity = activity;
    }
​
    @Override
    public void deductMoney() {
        System.out.println("奖品发送完了,请下次再参加");
    }
​
    @Override
    public boolean raffle() {
        System.out.println("奖品发送完了,请下次再参加");
        return false;
​
    }
​
    @Override
    public void dispensePrize() {
        System.out.println("奖品发送完了,请下次再参加");
    }
}

DispenseState

/**
 * 发放奖品的状态
 * 
 * @author Administrator
 *
 */
public class DispenseState extends State {
​
    //  初始化时传入活动引用,发放奖品后改变其状态
    RaffleActivity activity;
​
    public DispenseState(RaffleActivity activity) {
        this.activity = activity;
    }
​
    // 
​
    @Override
    public void deductMoney() {
        System.out.println("不能扣除积分");
    }
​
    @Override
    public boolean raffle() {
        System.out.println("不能抽奖");
        return false;
    }
​
    //  发放奖品@Override
    public void dispensePrize() {
        if (activity.getCount() > 0) {
            System.out.println("恭喜中奖了");
            //  改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
        } else {
            System.out.println("很遗憾,奖品发送完了");
            //  改变状态为奖品发送完毕, 后面我们就不可以抽奖
            activity.setState(activity.getDispensOutState());
            //  System.out.println("抽奖活动结束");
            //  System.exit(0);
        }
​
    }
}

RaffleActivity

/**
 * 抽奖活动
 */
public class RaffleActivity {
​
    //  state 表示活动当前的状态,是变化
    State state = null;
​
    //  奖品数量
    int count = 0;
​
    //  四个属性,表示四种状态
    State noRafflleState = new NoRaffleState(this);
    State canRaffleState = new CanRaffleState(this);
​
    State dispenseState = new DispenseState(this);
    State dispensOutState = new DispenseOutState(this);
​
    //  构造器
    //  1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
    //  2. 初始化奖品的数量
    public RaffleActivity(int count) {
        this.state = getNoRafflleState();
        this.count = count;
    }
​
    //  扣分, 调用当前状态的 deductMoney
    public void debuctMoney() {
        state.deductMoney();
​
    }
​
    //  抽奖
    public void raffle() {
        //  如果当前的状态是抽奖成功
​
        if (state.raffle()) {
            //  领取奖品
            state.dispensePrize();
        }
​
    }
​
    public State getState() {
        return state;
    }
​
    public void setState(State state) {
        this.state = state;
    }
​
    //  这里请大家注意,每领取一次奖品,count--
    public int getCount() {
        int curCount = count;
        count--;
        return curCount;
    }
​
    public void setCount(int count) {
        this.count = count;
    }
​
    public State getNoRafflleState() {
        return noRafflleState;
    }
​
    public void setNoRafflleState(State noRafflleState) {
        this.noRafflleState = noRafflleState;
    }
​
    public State getCanRaffleState() {
        return canRaffleState;
    }
​
    public void setCanRaffleState(State canRaffleState) {
        this.canRaffleState = canRaffleState;
    }
​
    public State getDispenseState() {
        return dispenseState;
    }
​
    public void setDispenseState(State dispenseState) {
        this.dispenseState = dispenseState;
    }
​
    public State getDispensOutState() {
        return dispensOutState;
​
    }
​
    public void setDispensOutState(State dispensOutState) {
        this.dispensOutState = dispensOutState;
    }
}

State

public abstract class State {
​
    //  扣除积分 - 50
    public abstract void deductMoney();
​
    //  是否抽中奖品
    public abstract boolean raffle();
​
    //  发放奖品
    public abstract void dispensePrize();
​
}

5 优点与缺点

5.1 优点

  • 可读性强:代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。
  • 状态隔离:每个状态都是一个单独的类,隔离了不同状态的处理逻辑。
  • 减少依赖:将各种状态的转换逻辑分布到状态子类中,减少了相互依赖。
  • 扩展简单:增加新的状态操作简单,只需添加一个新的状态子类,符合“开闭原则”。
  • 方便维护:将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错。

5.2 缺点

  • 类数量增加:如果状态数量较多,每个状态都要一个对应的类,状态类的数量会急剧增加,导致系统变得复杂,加大维护难度
  • 实现复杂:模式结构和实现相对复杂,需要较多的类和接口支持。

6 应用场景与实例

6.1 场景应用

        适用于一个对象存在多个状态且状态可以相互转换的场景,如播放器控制、订单处理、电梯运行等。例如,视频播放器中的播放、暂停、快进、停止等状态都可以用状态模式来管理。

6.2 具体实例

        在视频播放器中,将播放、暂停、快进、停止等状态封装成独立的状态类,并在上下文中维护当前状态。这样,当用户操作播放器时,只需改变当前状态对象即可改变播放器的行为。

7 总结

        综上所述,状态模式通过将每个状态封装成独立的类,有效管理和简化了对象的状态转换和行为变化。尽管会增加类的数目和维护复杂度,但在处理复杂状态逻辑时提供了一种灵活且可扩展的设计方案。

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建型模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 单例模式(Singleton Pattern) - 原型模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构型模式(Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为型模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发型模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值