设计模式学习第二十一节 状态模式

状态模式是一种行为设计模式,用于处理对象在多种状态之间的转换,并根据状态改变其行为。该模式将状态和行为封装在独立的类中,使得在不同状态下对象的行为变化对客户端透明。案例展示了如何在抽奖活动中根据用户VIP等级调整获奖概率,通过状态模式实现了状态的平滑转换。总结了状态模式的优点如代码可读性增强、封装状态转换规则和便于维护,同时也指出了可能产生的过多类和结构复杂性等问题。
摘要由CSDN通过智能技术生成

状态模式

概述

    基本介绍
    1、状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同行为的问题。状态和行为是一一对应的,状态之间可以相互转换。
    2、当一个对象的内部状态发送变化时,允许改变其行为,对象看起来似乎修改了它的类。状态模式是行为型模式。
    3、状态模式用于解决系统中复杂对象的状态转换,以及不同状态下行为的封装问题,当系统中的某个对象存在很多状态,这些状态之间可以进行转换,而且对象在不同状态下的拥有不同的行为。状态模式将一个对象的状态分离出去,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无需关心对象的状态转换以及当前所处的状态,客户端都可以进行一致的处理。
    原理类图
在这里插入图片描述
    Context:环境类也叫做上下文类,用于维护State实例,这个实例定义当前所有状态。
    State:是抽象状态类,定义一个接口封装与Context的一个特定状态相关的行为,在抽象状态类中声明了各种不同的状态对应的方法。
    concreteState:具体状态类,为抽象状态类的子类,每一个子类实现与一个环境类一个状态相关的行为,每一个具体类对应环境类的具体状态,不同的具体状态类行为不同。

案例

    状态模式标准写法

package com.example.pattern.state;

import lombok.Getter;

/**
 * 状态模式
 *
 * @author zjt
 * @date 2021-01-07
 */
// 标准的状态模式写法
public interface State { // 抽象状态接口,定义一个方法处理与特定状态相关的行为 具体行为由子类实现

    void handle(Context context);

}

class ConcreteStateA implements State {

    @Override
    public void handle(Context context) {
        System.out.println("状态1 handle 被调用");
        context.setState(new ConcreteStateB()); //设置A的下一个状态是B
    }
}

class ConcreteStateB implements State {

    @Override
    public void handle(Context context) {
        System.out.println("状态2 handle 被调用");
        context.setState(new ConcreteStateA());//设置B的下一个状态是A
    }
}

@Getter
class Context { // 环境类 定义当前的状态

    private State state;

    public Context() {
        this.state = new ConcreteStateA(); //定义Context的初始状态
    }

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

    public void operation() {
        state.handle(this); //对请求做处理并且指向下一个状态
    }

}

class Client {

    public static void main(String[] args) {
        Context context = new Context();
        for (int i = 0; i < 10; i++) {
            context.operation();
        }
    }

}

    案例代码实现
    关于案例我想了很久,在网上也找了一些案例,总感觉是有些案例为了使用一些模式而设计代码,有些不合适,GOF在《设计模式》书中状态模式举例的是非常合适的,还有设计模式之行为模式这篇博客总结的也到位,我自己也想了一个不是很合适的案例

package com.example.pattern.state.improve;

import lombok.Getter;
import lombok.Setter;

/**
 * 状态简单模式案例
 * <p>
 * 案例简介:
 * 当前有一个抽奖活动
 * 根据用户的VIP等级 获取奖品的价值区间概率也不同
 *
 * @author zjt
 * @date 2021-01-07
 */
@Getter
@Setter
public class UserH {

    private Integer level; // 表示会员等级 有4个等级

    // 模拟获得大奖的概率 不使用状态模式 使用 if 或者 switch case 方式
    // 如果算法复杂而且在一个类中全部实现 将来改动 可维护性较差
    // 类中的状态不固定,这个方法如果需要经常改动,我认为状态模式并不适合
    public void luckyDraw() {
        switch (this.level) {
            case 1:
                System.out.println("VIP1级会员 奖品价值 (0-1)元的概率80%;19%的概率 1-2元 ; 1%概率 2到10元");
                break;
            case 2:
                System.out.println("VIP2级会员 奖品价值 (0-1)元的概率70%;25%的概率 1-2元 ; 5%概率 2到10元");
                break;
            case 3:
                System.out.println("VIP1级会员 奖品价值 (0-1)元的概率60%;30%的概率 1-2元 ; 10%概率 2到10元");
                break;
            case 4:
                System.out.println("VIP1级会员 奖品价值 (0-1)元的概率50%;30%的概率 1-2元 ; 20%概率 2到10元");
                break;
            default:
                System.out.println("仅限会员用户参加");
                break;
        }
    }
}

@Getter
@Setter
class User {

    private State state;

    public User(State state) {
        this.state = state;
    }

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

interface State {
    void luckyDraw(User user);
}

class VIP1 implements State {

    private int count = 0;

    @Override
    public void luckyDraw(User user) {
        System.out.println("VIP1级会员 奖品价值 (0-1)元的概率80%;19%的概率 1-2元 ; 1%概率 2到10元");
        if (++count >= 10) { // 用户已经抽奖超过10次 提升抽奖概率
            System.out.println(" 用户已经抽奖10次 临时提升抽奖概率");
            user.setState(new VIP2());
        }
    }
}

class VIP2 implements State {

    private int count = 0;

    @Override
    public void luckyDraw(User user) {
        System.out.println("VIP2级会员 奖品价值 (0-1)元的概率70%;25%的概率 1-2元 ; 5%概率 2到10元");
    }
}

class Client {
    public static void main(String[] args) {
        User user = new User(new VIP1());
        for (int i = 0; i < 15; i++) {
            user.luckyDraw();
        }
    }
}


总结

    状态模式将一个对像在不同状态下的不同行为封装到一个一个的类中,通过设置不同状态对象而拥有不同的行为,而状态转换的细节对于客户端来说是透明的,方便客户端的使用。
    优点
    1、代码的可读性增强,状态模式将每个状态的行为封装到一个类中,只需要注入一个不同状态的对象就可以使对像拥有不同的行为。
    2、封装了状态转换的规则,在状态模式中可以将类型转换封装到环境类或者具体的状态类中,进行集中管理,而不是分散到方法中。
    3、方便维护,将判断语句移除了,如果每个状态的行为放到一个类中,每次调用方法时都要判断是什么状态,会产生许多的判断语句而且容易出错。
    缺点
    1、状态模式会产生很多的类,每一个状态都会产生一个类,当状态过多时,加大了维护的难度。
    2、状态模式的结构与实现比较复杂,如果使用不当会导致程序结构和代码的混乱,增加系统设计的难度。
    3、如果使用环境类去维护状态的转换,对开闭原则的支持也不是很好,如果增加一个中间状态类,无法转换到新增的状态,修改某个类的行为也需要修改对于类的源代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值