Java设计模式:状态模式

❤ 作者主页:欢迎来到我的技术博客😎
❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~*
🍊 如果文章对您有帮助,记得关注点赞收藏评论⭐️⭐️⭐️
📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉

一、状态模式的定义

状态模式(State Pattern)是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。该模式将对象的行为封装在不同的状态类中,使得在不同状态下可以选择不同的行为,从而使对象看起来好像修改了其类。


二、未使用状态模式的案例

在引入状态模式之前,我们先来演示一下没有使用状态模式的业务场景。

业务:在一个车辆租赁应用中,车辆的状态有四种:可租用、已出租、维修中、已归还。现在要实现进行车辆状态之间状态的互相切换,每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果车辆处于可租用状态,则可以执行出租、维修状态,如果车辆处于维修状态,则可以执行可出租、归还操作。

类图如下:
在这里插入图片描述
 

具体的类设计如下:

车辆租赁接口类:

public interface ICarRent {

    //车辆的4种状态
    public final static int AvailableState = 1; //可租用状态
    public final static int RentedState = 2; //已出租状态
    public final static int InServiceState = 3; //维修中状态
    public final static int ReturnedState = 4; //已归还状态

    //设置车辆的状态
    public void setState(int state);

    //车辆的动作
    public void Available();
    public void Rented();
    public void InService();
    public void Returned();

}

车辆租赁接口实现类:

public class CarRent implements ICarRent {

    private int state;

    @Override
    public void setState(int state) {
        this.state = state;
    }

    //执行可租用动作
    @Override
    public void Available() {
        switch (this.state) {
            case AvailableState :
                System.out.println("车辆无法重复处于可租用状态");
                break;
            case RentedState :
                System.out.println("车辆已出租");
                this.setState(RentedState); //将车辆的状态由可租用变成已出租
                break;
            case InServiceState :
                System.out.println("车辆处于维修中");
                this.setState(InServiceState);
                break;
            case ReturnedState :
                System.out.println("车辆无法处于已归还状态");
                break;
        }
    }

    //执行已出租动作
    @Override
    public void Rented() {
        switch (this.state) {
            case AvailableState :
                System.out.println("车辆无法处于可租用状态");
                break;
            case RentedState :
                System.out.println("车辆无法重复处于已租用状态");
                break;
            case InServiceState :
                System.out.println("车辆处于维修中");
                this.setState(InServiceState);
                break;
            case ReturnedState :
                System.out.println("车辆已归还");
                this.setState(ReturnedState);
                break;
        }
    }

    //执行维修中动作
    @Override
    public void InService() {
        switch (this.state) {
            case AvailableState :
                System.out.println("车辆无法处于可租用状态");
                break;
            case RentedState :
                System.out.println("车辆无法处于已租用状态");
                break;
            case InServiceState :
                System.out.println("车辆无法处于重复处于维修状态");
                break;
            case ReturnedState :
                System.out.println("车辆已归还");
                this.setState(ReturnedState);
                break;
        }
    }

    //执行已归还动作
    @Override
    public void Returned() {
        switch (this.state) {
            case AvailableState :
                System.out.println("车辆可租用");
                this.setState(AvailableState);
                break;
            case RentedState :
                System.out.println("车辆已出租");
                this.setState(RentedState); 
                break;
            case InServiceState :
                System.out.println("车辆处于维修中");
                this.setState(InServiceState);
                break;
            case ReturnedState :
                System.out.println("车辆无法重复处于维修状态");
                break;
        }

    }
}

客户端类:

public class client {

    public static void main(String[] args) {
        
        CarRent carRent = new CarRent();
        
        carRent.setState(ICarRent.AvailableState);
        
        carRent.Available();
        carRent.Rented();
        carRent.InService();
        carRent.Returned();
    }
}

通过上述代码,我们可以发现已经可以满足业务需求了,但是这种实现方式会存在下面的问题:

  • 使用了大量的 switch...case 这样的逻辑判断(if…else也是一样),使程序的可阅读性很差;
  • 如果新增了新的状态的话,则需要去修改上面的判断逻辑,可扩展很差;

因此,我们可以通过使用 状态模式 就可以很好地解决上述问题。


三、状态模式的结构

状态模式包含以下主要角色:

  • 环境(Context)角色: 也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
  • 抽象状态(State)角色: 定义一个接口,用于封装环境对象中的特定状态所对应的行为。
  • 具体状态(ConCrete State)角色: 实现抽象状态所对应的行为。

四、状态模式的实现

使用状态模式对上述案例进行改造,类图如下:
在这里插入图片描述
 
具体的类设计如下:

抽象状态类:

public abstract class CarState {

    //声明环境角色类变量
    protected Context context;

    public void setContext(Context context) {
        this.context = context;
    }

    //车辆可租用操作
    public abstract void Available();

    //车辆已出租操作
    public abstract void Rented();

    //车辆维修中操作
    public abstract void InService();

    //车辆已归还操作
    public abstract void Returned();

}

车辆可租用状态类:

public class AvailableState extends CarState {

    @Override
    public void Available() {
        System.out.println("车辆无法重复处于可租用状态");
    }

    @Override
    public void Rented() {
        System.out.println("车辆已出租");
        super.context.setCarState(Context.RENTED_STATE);
        super.context.Rented();
    }

    @Override
    public void InService() {
        System.out.println("车辆处于维修中");
        super.context.setCarState(Context.IN_SERVICE_STATE);
        super.context.InService();
    }

    @Override
    public void Returned() {
        System.out.println("车辆无法处于已归还状态");
    }
}

车辆已出租状态类:

public class RentedState extends CarState {

    @Override
    public void Available() {
        System.out.println("车辆无法处于可租用状态");
    }

    @Override
    public void Rented() {
        System.out.println("车辆无法重复处于已租用状态");
    }

    @Override
    public void InService() {
        System.out.println("车辆处于维修中");
        super.context.setCarState(Context.IN_SERVICE_STATE);
        super.context.InService();
    }

    @Override
    public void Returned() {
        System.out.println("车辆已归还");
        super.context.setCarState(Context.RETURNED);
        super.context.Returned();
    }
}

车辆维修中状态类:

public class InServiceState extends CarState {

    @Override
    public void Available() {
        System.out.println("车辆无法处于可租用状态");
    }

    @Override
    public void Rented() {
        System.out.println("车辆无法处于已租用状态");
    }

    @Override
    public void InService() {
        System.out.println("车辆无法处于重复处于维修状态");
    }

    @Override
    public void Returned() {
        System.out.println("车辆已归还");
        super.context.setCarState(Context.RETURNED);
        super.context.Returned();
    }
}

车辆已归还状态类:

public class Returned extends CarState {

    @Override
    public void Available() {
        System.out.println("车辆可租用");
        super.context.setCarState(Context.AVAILABLE_STATE);
        super.context.InService();
    }

    @Override
    public void Rented() {
        System.out.println("车辆已出租");
        super.context.setCarState(Context.RENTED_STATE);
        super.context.Rented();
    }

    @Override
    public void InService() {
        System.out.println("车辆处于维修中");
        super.context.setCarState(Context.IN_SERVICE_STATE);
        super.context.InService();
    }

    @Override
    public void Returned() {
        System.out.println("车辆无法重复处于维修状态");
    }
}

环境类:

public class Context {

    //定义对应状态对象的常量
    public final static AvailableState AVAILABLE_STATE = new AvailableState();
    public final static RentedState RENTED_STATE = new RentedState();
    public final static InServiceState IN_SERVICE_STATE = new InServiceState();
    public final static Returned RETURNED = new Returned();


    //定义一个当前车辆状态变量
    private CarState carState;

    public CarState getCarState() {
        return carState;
    }

    //设置当前状态对象
    public void setCarState(CarState carState) {
        this.carState = carState;
        //设置当前状态对象中的Context对象
        this.carState.setContext(this);
    }

    public void Available() {
        this.Available();
    }

    public void Rented() {
        this.Rented();
    }

    public void InService() {
        this.InService();
    }

    public void Returned() {
        this.Returned();
    }

}

客户端类:

public class Client {

    public static void main(String[] args) {

        CarRent carRent = new CarRent();

        carRent.setState(ICarRent.AvailableState);

        carRent.Available();
        carRent.Rented();
        carRent.InService();
        carRent.Returned();
    }
}

测试结果:
在这里插入图片描述
 


五、状态模式的优缺点

优点:

  1. 封装性强: 状态模式将每个状态的行为封装在独立的类中,使得每个状态的实现相对独立,提高了代码的封装性。
  2. 可扩展性: 可以方便地添加新的状态类,而不需要修改已有的代码,系统的可扩展性得到提高。
  3. 简化条件判断: 状态模式通过将状态的判断转移到状态类中,避免了大量的条件判断语句,提高了代码的可读性和维护性。
  4. 提高了上下文类的间接性: 上下文类中不再包含复杂的条件判断语句,仅负责委托给当前状态类处理。
  5. 状态切换更灵活: 状态模式使得状态切换变得更加灵活,状态之间的切换由具体的状态类负责,可以在不影响其他状态的情况下单独修改某一状态的行为。

缺点:

  1. 类数量增加: 引入状态模式会增加系统中类的数量,特别是当状态很多时,会导致类的数量呈指数级增长。
  2. 状态切换过多: 如果状态之间的切换比较频繁,可能会导致系统变得复杂难以维护。
  3. 上下文类的状态转换逻辑: 上下文类中的状态转换逻辑可能会分散到各个状态类中,使得理解整个状态转换流程变得复杂。
  4. 不适用于简单的状态: 当系统中的状态很简单,且状态之间的切换较为简单时,引入状态模式可能显得繁琐,不划算。

六、状态模式的使用场景

  1. 对象的行为依赖于其状态,并且在运行时可以改变状态: 当一个对象在不同状态下有不同的行为,并且这些行为可以在运行时动态地改变,状态模式就能够很好地解决这种情况。
  2. 有大量条件语句判断对象的状态: 如果在代码中存在大量的条件语句用于判断对象的状态,并且这些条件语句导致代码复杂且难以维护,那么状态模式可以用于优化代码结构。
  3. 状态切换的逻辑相对复杂: 当状态之间的转换逻辑比较复杂,而且在不同状态下可能有不同的行为,使用状态模式可以将状态转换逻辑封装在状态类中,使代码更加清晰。
  4. 一个对象存在多个状态且状态之间相互转换: 如果一个对象有多个状态,并且这些状态之间存在相互转换的关系,使用状态模式可以更好地管理这种状态转换。
  5. 需要动态添加新状态: 如果系统需要在运行时动态地添加新的状态,并且这些新状态可能导致行为的改变,状态模式可以提供良好的扩展性。
  6. 避免使用过多的条件判断语句: 当需要避免过多的条件判断语句,提高代码的可读性和可维护性时,状态模式可以将状态相关的代码分散到不同的状态类中,使得代码更加清晰。

七、状态模式和策略模式的区别

状态模式和策略模式的UML类图几乎一模一样,容易在初学时混淆。然而,它们有一些相似之处,但也存在着明显的区别。

状态模式(State Pattern):

  1. 关注对象内部状态的改变: 状态模式主要关注对象在不同状态下的行为变化,对象的行为随着内部状态的改变而改变。
  2. 状态切换由上下文控制: 状态模式中,状态的切换是由上下文(Context)类控制的,上下文类维护一个当前状态的引用,并在状态发生改变时委托给相应的状态类。
  3. 状态间有转换关系: 状态模式适用于对象存在多个状态,且这些状态之间存在明确的转换关系。

策略模式(Strategy Pattern):

  1. 关注算法的替换: 策略模式主要关注定义一系列算法,使它们可以互相替换,使得算法的选择可以独立于使用算法的客户端。
  2. 客户端决定使用哪个策略: 策略模式中,客户端决定使用哪个具体策略,客户端持有一个策略接口的引用,而不是持有具体策略类的引用。
  3. 算法间无转换关系: 策略模式适用于需要动态地在多个算法中选择一个的情况,这些算法之间可能没有明确的转换关系。

区别总结:

  1. 关注点不同: 状态模式关注对象在不同状态下的行为变化,而策略模式关注算法的替换和客户端如何选择使用算法。
  2. 状态切换控制: 在状态模式中,状态的切换由上下文控制;而在策略模式中,客户端控制使用哪个策略。
  3. 状态间关系: 状态模式适用于对象存在多个状态且状态间有明确的转换关系;策略模式适用于多个算法可以替换且客户端需要动态选择算法的情况。

 
非常感谢您阅读到这里,如果这篇文章对您有帮助,希望能留下您的点赞👍 关注💖 分享👥 留言💬thanks!!!

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java技术一点通

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

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

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

打赏作者

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

抵扣说明:

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

余额充值