IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 从零开始学习Java设计模式 | 行为型模式篇:状态模式 -> 正文阅读

[Java知识库]从零开始学习Java设计模式 | 行为型模式篇:状态模式

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

状态模式引入案例

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

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

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

在这里插入图片描述

从以上类图中可以看到,我们首先定义了一个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接口里面定义一个表示断电状态的常量,然后再定义一个抽象的方法,接下来,在子类中还要去重写这个方法,并且对于前面已经定义好的四个方法也要进行一个修改,所以程序的扩展性是非常差的。

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

概述

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

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

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

开宗明义,状态模式说的是有状态的对象,所以对于没有状态的对象,你就不能使用状态模式了。然后,状态模式说的是把复杂的"判断逻辑"提取到不同的状态对象中,对于这句话,我们该如何理解呢?试想一下我们之前的做法,是不是在每一个方法里面使用switch case或者if else语句来进行判断的啊?现在使用状态模式就不同了,我们会避免使用switch case或者if else这些判断语句,而是把它们提取到不同的状态对象中,通过面向对象的形式,把同样的逻辑给实现出来。

最后,状态模式说的是允许状态对象在其内部状态发生改变时改变其行为,对于这句话,我们该如何理解呢?举例来说,在上述电梯案例中,如果当前电梯现正处于运行状态中,那么我们还能执行开门的操作吗?很显然,肯定是不可以的,这也正说明了当前电梯状态发生改变会改变其行为。

以上就是我对状态模式概念的解释,如果大家还有什么不懂的,后面我会再通过一个案例来向大家进行详细的说明。

结构

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

  • 环境(Context)角色:也称为上下文,它定义了客户程序需要的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。

    注意,环境角色只是对外提供了一个接口,而且在它里面维护了一个当前状态,所以,具体执行操作的还是对应的当前状态对象。

  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。

  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为。

上面三个角色你不通过案例来理解,相信你是理解不了的,除非你是天选之子!这里我就不妨先举个例子来说说吧!还是以上述电梯案例来说,如果当前电梯正处于运行状态,那么我们就要创建一个电梯运行状态类了,另外还得去创建它的对象,并将该对象维护在环境角色里面。

状态模式案例

接下来,我们便通过状态模式来改进以上电梯案例,以此希望大家对状态模式的概念以及它里面所具有的角色有一个更深入的理解。

分析

对上述电梯案例使用状态模式进行改进之后的类图如下所示。

在这里插入图片描述

接下来,我们就要分析一下以上类图中所涉及到的类,以及类和类之间的关系了。

可以看到,最上面有一个LiftState类,它充当的就是状态模式里面的抽象状态角色。在该类里面,我们声明了一个Context类型的成员变量,且它是protected修饰的,也就意味着子类可以直接去使用它了。那Context又是啥呢?它是环境角色类,在类图的最下面大家也能看到它,等一会我们再去详细说它啊!

另外,在LiftState类里面我们还提供了一个setContext方法,它是用来设置环境角色类对象的。除了它之外,我们还提供了四个操作电梯状态的方法,分别是open、close、stop和run等方法,而且它们都是抽象的,既然是抽象的,那就意味着要求子类必须去实现了。

然后,我们再来看一下LiftState类的四个子类,它们都是具体状态类,分别是电梯开启状态类、电梯关闭状态类、电梯停止状态类以及电梯运行状态类。对于这四个子类而言,它们必须去重写父类中的四个抽象方法。

最后,我们来看一下Context类,它充当的是状态模式里面的环境角色。在该类里面,我们定义了四个具体状态类对象的常量,除此之外,我们还定义了一个LiftState类型的成员变量,它是用来记录当前电梯状态的,并且为它我们也提供了对应的getter和setter方法。

当然了,在该类里面,我们还提供了open、close、stop以及run等这些方法,那你知道这些方法主要是来干嘛的吗?

看一下状态模式里面对环境角色的描述,它说的是将与状态相关的操作委托给当前状态对象来处理,所以,在Context类的open、close、stop以及run这些方法里面,到时候就会去调用当前状态对象里面对应的方法了。

实现

首先,打开咱们的maven工程,并在com.meimeixia.pattern包下新建一个子包,即state.after,也即使用状态模式对电梯案例改进的具体代码我们是放在了该包下。

然后,创建抽象状态类,即LiftState。

package com.meimeixia.pattern.state.after;

/**
 * 抽象状态类
 * @author liayun
 * @create 2021-09-17 11:58
 */
public abstract class LiftState {

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

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

    // 电梯开启操作
    public abstract void open();

    // 电梯关闭操作
    public abstract void close();

    // 电梯运行操作
    public abstract void run();

    // 电梯停止操作
    public abstract void stop();

}

接着,创建四个具体状态类。这里,先创建电梯开启状态类,即OpeningState。

package com.meimeixia.pattern.state.after;

/**
 * 电梯开启状态类
 * @author liayun
 * @create 2021-09-17 12:07
 */
public class OpeningState extends LiftState {

    /**
     * 当前状态下要执行的方法
     *
     * 因为当前电梯本身就处于开启状态,所以open方法就是当前电梯在开启状态下要执行的方法
     */
    @Override
    public void open() {
        System.out.println("电梯开启...");
    }

    /**
     * 当电梯处于开启状态时,我们是可以关闭电梯门的
     */
    @Override
    public void close() {
        // 修改状态
        super.context.setLiftState(Context.CLOSING_STATE); // 记录当前电梯的状态
        // 调用当前电梯状态对象中的Context对象中的close方法。记住,此时调用的是电梯关闭状态对象中的close方法
        super.context.close();
    }

    /**
     * 电梯门不能开着就跑,所以这里我们什么也不做
     */
    @Override
    public void run() {
        // 什么都不做
    }

    /**
     * 电梯处于开启状态时,它本身就是停止的了,所以这里我们什么也不做
     */
    @Override
    public void stop() {
        // 什么都不做
    }
    
}

电梯开启状态类创建完毕之后,接下来其他的具体状态类就比较容易创建了,照葫芦画瓢呗!

创建的电梯运行状态类,即RunningState,代码如下所示。

package com.meimeixia.pattern.state.after;

/**
 * 电梯运行状态类
 * @author liayun
 * @create 2021-09-17 12:07
 */
public class RunningState extends LiftState {

    /**
     * 电梯运行的时候开电梯门?你疯了吗!电梯它不会给你开的,所以这里我们什么也不做
     */
    @Override
    public void open() {
        // do nothing
    }

    /**
     * 当电梯处于运行状态时,电梯门本身就是关闭的,所以这里我们什么也不做
     */
    @Override
    public void close() {
        // do nothing
    }

    /**
     * 当前状态下要执行的方法
     *
     * 因为当前电梯本身就处于运行状态,所以run方法就是当前电梯在运行状态下要执行的方法
     */
    @Override
    public void run() {
        System.out.println("电梯正在运行...");
    }

    /**
     * 这个事绝对是合理的,电梯光运行不停止,那还有谁敢坐这个电梯啊?估计只有上帝了
     */
    @Override
    public void stop() {
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.stop();
    }

}

创建的电梯停止状态类,即StoppingState,代码如下所示。

package com.meimeixia.pattern.state.after;

/**
 * 电梯停止状态类
 * @author liayun
 * @create 2021-09-17 12:07
 */
public class StoppingState extends LiftState {

    /**
     * 当电梯处于停止状态时,我们是可以开电梯门的
     */
    @Override
    public void open() {
        // 状态修改
        super.context.setLiftState(Context.OPENING_STATE);
        /*
         * 委托给OpeningState类来执行开电梯门这个动作
         *
         * 当然了,以下代码你也可以替换为:super.context.open();
         */
        super.context.getLiftState().open();
    }

    /**
     * 当电梯处于停止状态时,我们也是可以关电梯门的
     */
    @Override
    public void close() {
        // 状态修改
        super.context.setLiftState(Context.CLOSING_STATE);
        /*
         * 委托给ClosingState类来执行关电梯门这个动作
         *
         * 当然了,以下代码你也可以替换为:super.context.close();
         */
        super.context.getLiftState().close();
    }

    /**
     * 电梯由停止状态再跑起来,正常的很
     */
    @Override
    public void run() {
        // 状态修改
        super.context.setLiftState(Context.RUNNING_STATE);
        /*
         * 委托给RunningState类来执行运行电梯这个动作
         *
         * 当然了,以下代码你也可以替换为:super.context.run();
         */
        super.context.getLiftState().run();
    }

    /**
     * 当前状态下要执行的方法
     *
     * 因为当前电梯本身就处于停止状态,所以stop方法就是当前电梯在停止状态下要执行的方法
     */
    @Override
    public void stop() {
        System.out.println("电梯停止了...");
    }

}

创建的电梯关闭状态类,即ClosingState,代码如下所示。

package com.meimeixia.pattern.state.after;

/**
 * 电梯关闭状态类
 * @author liayun
 * @create 2021-09-17 12:07
 */
public class ClosingState extends LiftState {

    /**
     * 当前状态下要执行的方法
     *
     * 因为当前电梯本身就处于关闭状态,所以close方法就是当前电梯在关闭状态下要执行的方法
     */
    @Override
    public void close() {
        System.out.println("电梯门关闭...");
    }

    /**
     * 电梯门关了再打开,这是允许的
     */
    @Override
    public void open() {
        super.context.setLiftState(Context.OPENING_STATE);
        super.context.open();
    }

    /**
     * 电梯门关了就跑,这是再正常不过的了
     */
    @Override
    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.run();
    }

    /**
     * 电梯门关着,我就是不按楼层,你能怎么着我
     */
    @Override
    public void stop() {
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.stop();
    }

}

以上四个具体状态类创建完毕之后,接下来,我们来创建环境角色类,即Context。

package com.meimeixia.pattern.state.after;

/**
 * 环境角色类
 * @author liayun
 * @create 2021-09-17 12:02
 */
public class Context {

    // 定义对应电梯状态类对象的常量
    public final static OpeningState OPENING_STATE = new OpeningState();
    public final static ClosingState CLOSING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = new StoppingState();

    // 定义一个记录当前电梯状态的变量
    private LiftState liftState;

    public LiftState getLiftState() {
        return liftState;
    }

    // 设置当前电梯状态对象
    public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        // 设置完当前电梯状态对象之后,别忘了,我们还得设置当前电梯状态对象中的Context对象
        this.liftState.setContext(this); // 现在我们就在Context类中,所以在setContext方法里面直接传递this就可以了
    }

    // 以下是四个操作电梯状态的方法,在这些方法里面我们都是直接去调用当前电梯状态对象里面各自对应的方法
    public void open() {
        this.liftState.open();
    }

    public void close() {
        this.liftState.close();
    }

    public void run() {
        this.liftState.run();
    }

    public void stop() {
        this.liftState.stop();
    }

}

最后,我们创建一个客户端类来用于测试。

package com.meimeixia.pattern.state.after;

/**
 * @author liayun
 * @create 2021-09-17 16:25
 */
public class Client {
    public static void main(String[] args) {
        // 创建环境角色对象,因为我们之前就已经说过,环境角色就是对外提供访问的接口
        Context context = new Context();
        // 设置当前电梯状态
        context.setLiftState(new RunningState());

        context.open();
        context.close();
        context.run();
        context.stop();
    }
}

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

在这里插入图片描述

当前电梯正处于运行状态,那么你就不能再去开电梯门了,要是允许你去开电梯门,那这得多危险啊!所以,当程序执行完context.open()这行代码时,你会发现压根就没有打印任何结果。

这时我们还能去关闭电梯门吗?电梯运行时,门本身就关闭着,你再去关闭电梯门,请问有什么意义呢?所以,当程序执行完context.close()这行代码时,你同样会发现没有打印任何结果。

然后,当程序执行完context.run()这行代码时,我们终于看到相应的输出结果了,这是因为run方法就是当前电梯在运行状态下要执行的方法。

接着,由于电梯在运行过程中,可以停下来,所以当程序执行完context.stop()这行代码时,我们也是能看到相应的输出结果的,只不过此时当前电梯的状态已经变成了停止状态。

至此,使用状态模式改进的电梯案例我们就算是实现了。在改进后的电梯案例中,你会发现在我们写的代码里面并没有if else或者switch case这样的语句,而且程序的扩展性也很好,因为后期如果我们想要再添加一个断电状态,那么只需要再创建一个LiftState类的子类就可以了,当然了,Context环境角色类也要进行一个修改,只不过我们修改的地方会少很多。

状态模式的优缺点以及使用场景

接下来,我们来看一下状态模式的优缺点以及使用场景。

优缺点

优点

关于状态模式的优点,我总结出了下面两点。

  1. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。

  2. 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

    在未使用状态模式改进的电梯案例中,我们就大量使用到了switch case或者if else这样的语句,这就不是面向对象的思想,而使用了状态模式之后,是不是更加面向对象了啊!此外,它还允许将状态转换逻辑和状态对象合为一体,这不是一举两得吗?

缺点

关于状态模式的优点,我总结出了下面三点。

  1. 状态模式的使用必然会增加系统类和对象的个数。

    在未使用状态模式改进的电梯案例中,其实也就创建了一个接口和一个类,而使用了状态模式进行了改进之后,你会发现有多少个状态就要创建多少个状态类,这样,类肯定是增加了,类增加了之后,那么相应的对象的个数肯定也会增加。

  2. 状态模式的结构与实现都较为复杂,如果使用不当,那么就会导致程序结构和代码的混乱。

    看一下咱们使用状态模式改进之后的电梯案例,在抽象状态类(即LiftState)里面就聚合了Context类的对象,而在Context类里面,你会发现又聚合了LiftState类的对象,所以程序的结构还是比较复杂的,这样的话,我们在设计的时候就一定要加倍小心了。

  3. 状态模式对"开闭原则"的支持并不太好。

    你想啊,后期如果我们想要添加一个新的状态,那么我们不光要添加一个新的状态类,还要去修改Context类里面的代码,并且对于那些具体状态类的代码我们也要稍微进行一个修改,所以我们才说状态模式对开闭原则的支持并不是特别好。

使用场景

只要出现如下几个场景,我们就可以去考虑一下能不能使用状态模式了。

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式了。

    状态模式主要体现的就是状态,只要一个对象的行为取决于它的状态(状态不一样,对象的行为也会不一样),那么这个时候你就可以考虑使用状态模式了。

  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

    也就是说,如果你写的代码里面大量地运用到了switch case或者if else语句,那么这个时候你就不妨试试使用状态模式。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-30 11:48:08  更:2021-09-30 11:48:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 18:40:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码