从零开始学习java设计模式|行为型模式篇:状态模式(代码片段)

李阿昀 李阿昀     2023-01-07     394

关键词:

在本讲,我们来学习一下行为型模式里面的第五个设计模式,即状态模式。

状态模式引入案例

在学习状态模式之前,我们先来看一个案例,通过该案例来引出状态模式。

这个案例就是通过电梯按钮来控制一个电梯的状态。一个电梯有开门状态、关门状态、停止状态、运行状态等四种状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,那么就不能进行开门操作。为什么呢?你想啊,现在电梯正处于运行状态呢,然后门开了,这是不是非常危险呀!所以,当电梯处于运行状态时是不允许执行开门操作的。而如果电梯门是停止状态,那么就可以执行开门操作了。

下面我们就来看一下对于以上案例所设计出来的类图。

从以上类图中可以看到,我们首先定义了一个ILift接口,而且它里面声明有四个常量,即OPENING_STATE、CLOSING_STATE、RUNNING_STATE、STOPPING_STATE,它们分别是来表示电梯的四种状态的,即开启状态、关闭状态、运行状态和停止状态。

此外,ILift接口还提供了5个抽象方法,它们分别是:

  1. setState(int state):设置电梯的状态。因为我们总得记录一下当前电梯的一个状态吧!
  2. open():电梯开门的方法
  3. close():电梯关门的方法
  4. stop():电梯停止的方法
  5. run():电梯运行的方法

注意,ILift接口就是用于提高程序扩展性的,如果后期有其他的实体也拥有电梯的四种状态,那么我们完全可以让它去实现该接口。

然后,我们再来看一下ILift接口的子实现类,即Lift。可以看到,在该类里面定义了一个state属性,这个属性就是用来记录当前电梯状态的。除此之外,该类还重写了父接口中的所有抽象方法。

至于那个客户端类,我们就不用过多地去关注它了。所以,整个看下来,系统设计起来还是比较简单的。接下来,我们就得编写代码来实现以上案例了。

首先,打开咱们的maven工程,并在com.meimeixia.pattern包下新建一个子包,即state.before,也即实现以上案例的具体代码我们是放在了该包下。

然后,创建电梯接口,这里我们就命名为了ILift。

package com.meimeixia.pattern.state.before;

/**
 * 电梯接口
 * @author liayun
 * @create 2021-09-17 10:44
 */
public interface ILift 

    // 定义四个电梯状态的常量
    int OPENING_STATE = 1;
    int CLOSING_STATE = 2;
    int RUNNING_STATE = 3;
    int STOPPING_STATE = 4;

    // 设置电梯状态的功能
    void setState(int state);

    // 电梯操作功能
    void open();

    void close();

    void run();

    void stop();


接着,创建电梯接口的子实现类,即电梯类,这里我们就命名为了Lift。

package com.meimeixia.pattern.state.before;

/**
 * 电梯类(ILift接口的子实现类)
 * @author liayun
 * @create 2021-09-17 10:51
 */
public class Lift implements ILift 

    // 声明一个记录当前电梯状态的成员变量
    private int state;

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

    @Override
    public void open() 
        switch (state)  // 判断当前电梯的状态
            case OPENING_STATE:
                // 如果当前电梯正处于开启状态,那么我们再去开门,这就没有任何意义了,所以这儿我们什么事都不做
                break;
            case CLOSING_STATE:
                // 如果当前电梯正处于关闭状态,那么我们就能开电梯门了
                System.out.println("电梯打开了...");
                // 设置当前电梯状态为开启状态
                setState(OPENING_STATE);
                break;
            case STOPPING_STATE:
                // 如果当前电梯正处于停止状态,那么我们也是能开电梯门的
                System.out.println("电梯打开了...");
                // 设置当前电梯状态为开启状态
                setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                // 如果当前电梯正处于运行状态,那么我们肯定是不能去开门的,因为电梯运行时是不能开门的,所以这儿我们什么事都不做
                break;
        
    

    @Override
    public void close() 
        switch (this.state) 
            case OPENING_STATE:
                // 如果当前电梯正处于开启状态,那么我们就能关闭电梯门了
                System.out.println("电梯关门了...");
                // 设置当前电梯状态为关闭状态
                this.setState(CLOSING_STATE);
                break;
            case CLOSING_STATE:
                // 如果当前电梯正处于关闭状态,那么我们再去关门,这就没有任何意义了,所以这儿我们什么事都不做
                // do nothing
                break;
            case RUNNING_STATE:
                // 如果当前电梯正处于运行状态,很显然,此时电梯门肯定是关着的,那么我们就不能再去关门了,所以这儿我们什么事都不做
                // do nothing
                break;
            case STOPPING_STATE:
                // 如果当前电梯正处于停止状态,很显然,此时电梯门肯定也是关着的,那么我们就不能再去关门了,所以这儿我们什么事都不做
                // do nothing
                break;
        
    

    @Override
    public void run() 
        switch (this.state) 
            case OPENING_STATE:
                // 如果当前电梯正处于开启状态,那么我们肯定是不能让电梯运行的,因为电梯不能开着门就走,所以这儿我们什么事都不做
                // do nothing
                break;
            case CLOSING_STATE:
                // 如果当前电梯正处于关闭状态,那么我们就能让电梯运行了
                System.out.println("电梯开始运行了...");
                // 设置当前电梯状态为运行状态
                this.setState(RUNNING_STATE);
                break;
            case RUNNING_STATE:
                // 如果当前电梯正处于运行状态,那么我们再去运行电梯,这就没有任何意义了,所以这儿我们什么事都不做
                // do nothing
                break;
            case STOPPING_STATE:
                // 如果当前电梯正处于停止状态,那么我们也是可以让电梯运行的
                System.out.println("电梯开始运行了...");
                // 设置当前电梯状态为运行状态
                this.setState(RUNNING_STATE);
                break;
        
    

    @Override
    public void stop() 
        switch (this.state) 
            case OPENING_STATE:
                // 如果当前电梯正处于开启状态,那么我们再让电梯停止下来,就没有必要了,因为开门的电梯已经是停止的了(正常情况下),所以这儿我们什么事都不做
                // do nothing
                break;
            case CLOSING_STATE:
                // 如果当前电梯正处于关闭状态,那么我们就能让电梯停止下来了,因为电梯关门时才可以停止
                System.out.println("电梯停止了...");
                // 设置当前电梯状态为停止状态
                this.setState(STOPPING_STATE);
                break;
            case RUNNING_STATE:
                // 如果当前电梯正处于运行状态,那么我们也是能让电梯停止的,因为电梯运行时本身就可以停止啊
                System.out.println("电梯停止了...");
                // 设置当前电梯状态为停止状态
                this.setState(STOPPING_STATE);
                break;
            case STOPPING_STATE:
                // 如果当前电梯正处于停止状态,那么我们再去让电梯停止下来,这就没有任何意义了,所以这儿我们什么事都不做
                // do nothing
                break;
        
    


最后,创建客户端类用于测试。

package com.meimeixia.pattern.state.before;

/**
 * @author liayun
 * @create 2021-09-17 11:20
 */
public class Client 

    public static void main(String[] args) 
        // 创建电梯对象
        Lift lift = new Lift();

        // 设置当前电梯的状态
        lift.setState(ILift.OPENING_STATE);

        // 打开
        lift.open();
        lift.close();
        lift.run();
        lift.stop();
    

此时,运行以上客户端类,打印结果如下图所示,下面我就来解释一下为何会打印出这样的结果。

当前电梯正处于开启状态,于是你再去开电梯门,那就没有任何意义了,所以执行电梯的open方法去开电梯门时,你会发现并没有任何输出。但是,现在关闭电梯门是可行的,所以在执行电梯的close方法时,你就能看到相应的输出结果了,而且此时电梯的状态就变成关闭状态了。

当电梯处于关闭状态时,你就能让电梯运行起来了,所以在执行电梯的run方法时,你就能看到相应的输出结果了,而且此时电梯的状态又变成了运行状态。

当电梯处于运行状态时,能让电梯停止吗?当然可以,所以在执行电梯的stop方法时,你就能看到相应的输出结果了,而且此时电梯的状态又变成了停止状态。

大家试想一下,如果将当前电梯的状态设置为运行状态,那么打印的结果又会是什么呢?

你会发现只打印了一句话,为什么会这样呢?因为当前电梯正处于运行状态,那么此时是不允许你去开电梯门的,要是你在电梯运行的过程中开门那得多危险啊!那去关电梯门,可不可以呢?大可不必啊,因为电梯在运行过程中,本身电梯门就是关闭的,你再去关电梯门,不是有点脱裤子放屁的意思吗?那去运行电梯,可不可以呢?同样的道理啊,大可不必,因为电梯本身就在运行过程中,你再去运行电梯,那就没有必要了。那去让电梯停下来呢?此时就可以了,所以在执行电梯的stop方法时,你就能看到相应的输出结果了,即电梯停止了…

大家再想一下,我们上面设计的系统有没有什么问题啊?是不是有如下这样的问题啊!

  • 使用了大量的switch case这样的判断语句(当然了,有些人比较喜欢使用if else语句,不过效果都是一样),使程序的可阅读性变得特别差。尤其是咱们Lift类中的方法,你会发现阅读起来体验特别特别差。

  • 扩展性很差。如果新加了断电的状态,那么我们就需要修改上面的判断逻辑。

    其实,不光要修改上面的判断逻辑,我们还得在ILift接口里面定义一个表示断电状态的常量,然后再定义一个抽象的方法,接下来,在子类中还要去重写这个方法,并且对于前面已经定义好的四个方法也要进行一个修改,所以程序的扩展性是非常差的。

问题既然出现了,那么应该如何解决呢?嘿嘿,此时,我们就要使用状态模式了。那什么是状态模式呢?下面我就会讲到。

概述

上面我们做了一个电梯的案例,也引出了该电梯案例所存在的问题,并提出了解决方案,也就是使用状态模式来进行一个改进。那什么是状态模式呢?接下来,我们就来看一看它的概念。

对有状态的对象,把复杂的"判断逻辑"提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

大家猛然看到状态模式的概念会有点懵!不过没关系,下面我会向大家一句一句来解释。

从零开始学习java设计模式|行为型模式篇:备忘录模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第十个设计模式,即备忘录模式。概述在学习备忘录模式的概念之前,我们先来看下面这段描述。备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到... 查看详情

从零开始学习java设计模式|行为型模式篇:备忘录模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第十个设计模式,即备忘录模式。概述在学习备忘录模式的概念之前,我们先来看下面这段描述。备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到... 查看详情

从零开始学习java设计模式|行为型模式篇:模板方法模式(代码片段)

接下来,我们来学习第四章的内容,即行为型模式。简单聊聊行为型模式首先,我们来看一看什么是行为型模式。行为型模式用于描述程序在运行时复杂的流程控制(我们之前学习过很多流程控制语句,例如if... 查看详情

从零开始学习java设计模式|行为型模式篇:模板方法模式(代码片段)

接下来,我们来学习第四章的内容,即行为型模式。简单聊聊行为型模式首先,我们来看一看什么是行为型模式。行为型模式用于描述程序在运行时复杂的流程控制(我们之前学习过很多流程控制语句,例如if... 查看详情

从零开始学习java设计模式|行为型模式篇:命令模式

在本讲,我们来学习一下行为型模式里面的第三个设计模式,即命令模式。概述首先,我们先来看下这样一个场景:在日常生活中,我们出去吃饭都会遇到下面的场景。顾客把订单交给女服务员,女服务员... 查看详情

从零开始学习java设计模式|行为型模式篇:责任链模式

在本讲,我们来学习一下行为型模式里面的第四个设计模式,即责任链模式。概述在学习责任链模式之前,我们先来看一下下面这段描述。在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处... 查看详情

从零开始学习java设计模式|行为型模式篇:中介者模式

在本讲,我们来学习一下行为型模式里面的第七个设计模式,即中介者模式。概述在学习中介者模式之前,我们先来看下面这段描述。一般来说,同事类之间的关系是比较复杂的(其实,这儿所说的同事类... 查看详情

从零开始学习java设计模式|行为型模式篇:命令模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第三个设计模式,即命令模式。概述首先,我们先来看下这样一个场景:在日常生活中,我们出去吃饭都会遇到下面的场景。顾客把订单交给女服务员,女服务员... 查看详情

从零开始学习java设计模式|行为型模式篇:责任链模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第四个设计模式,即责任链模式。概述在学习责任链模式之前,我们先来看一下下面这段描述。在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处... 查看详情

从零开始学习java设计模式|行为型模式篇:策略模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第二个设计模式,即策略模式。概述先看下面的图片,我们去旅游选择出行方式能有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。作为一个程序猿... 查看详情

从零开始学习java设计模式|行为型模式篇:策略模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第二个设计模式,即策略模式。概述先看下面的图片,我们去旅游选择出行方式能有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。作为一个程序猿... 查看详情

从零开始学习java设计模式|行为型模式篇:解释器模式(代码片段)

在本讲,我们来学习一下行为型模式里面的最后一个设计模式,即解释器模式。总算是将这23种设计模式干完了,奥里给!概述在学习解释器模式之前,我们先来看下下面这张图。可以看到,这就是一个计... 查看详情

从零开始学习java设计模式|行为型模式篇:解释器模式(代码片段)

在本讲,我们来学习一下行为型模式里面的最后一个设计模式,即解释器模式。总算是将这23种设计模式干完了,奥里给!概述在学习解释器模式之前,我们先来看下下面这张图。可以看到,这就是一个计... 查看详情

从零开始学习java设计模式|行为型模式篇:观察者模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第六个设计模式,即观察者模式。概述我们先来看一看观察者模式的概念。观察者模式又被称为发布/订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系ÿ... 查看详情

从零开始学习java设计模式|行为型模式篇:观察者模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第六个设计模式,即观察者模式。概述我们先来看一看观察者模式的概念。观察者模式又被称为发布/订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系ÿ... 查看详情

从零开始学习java设计模式|行为型模式篇:迭代器模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第八个设计模式,即迭代器模式。概述相信大家对于迭代器并不会感到陌生,因为平时我们使用的还是比较多的,注意,在这儿我说的是使用。在开发过程中我们会... 查看详情

从零开始学习java设计模式|行为型模式篇:迭代器模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第八个设计模式,即迭代器模式。概述相信大家对于迭代器并不会感到陌生,因为平时我们使用的还是比较多的,注意,在这儿我说的是使用。在开发过程中我们会... 查看详情

从零开始学习java设计模式|行为型模式篇:中介者模式(代码片段)

在本讲,我们来学习一下行为型模式里面的第七个设计模式,即中介者模式。概述在学习中介者模式之前,我们先来看下面这段描述。一般来说,同事类之间的关系是比较复杂的(其实,这儿所说的同事类... 查看详情