设计模式 ~ 行为型模式 ~ 状态模式 ~ State Pattern。

设计模式 ~ 行为型模式 ~ 状态模式 ~ State Pattern。



概述。

【eg.】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。

类图如下。

在这里插入图片描述
代码如下。

package com.geek.state.patern.before;

/**
 * @author geek
 */
public interface ILift {

    // 电梯的 4 个状态。

    /**
     * 开门状态。
     */
    int OPENING_STATE = 1;
    /**
     * 关门状态。
     */
    int CLOSING_STATE = 2;
    /**
     * 运行状态。
     */
    int RUNNING_STATE = 3;
    /**
     * 停止状态。
     */
    int STOPPING_STATE = 4;

    /**
     * 设置电梯的状态。
     *
     * @param state
     */
    void setState(int state);

    // 电梯的动作。

    /**
     * 开门。
     */
    void open();

    /**
     * 关门。
     */
    void close();

    /**
     * 运行。
     */
    void run();

    /**
     * 停止。
     */
    void stop();

}

package com.geek.state.patern.before;

/**
 * 电梯。
 *
 * @author geek
 */
public class Lift implements ILift {

    private int state;

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

    /**
     * 关门。
     */
    @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;
            default:
                break;
        }
    }

    /**
     * 开门。
     */
    @Override
    public void open() {
        switch (this.state) {
            case OPENING_STATE:
                // do nothing.  // 门已经开了,不能再开门了。
                break;
            case CLOSING_STATE:
                // 关门状态,门打开。
                System.out.println("电梯开门。");
                this.setState(OPENING_STATE);
                break;
            case RUNNING_STATE:
                // do nothing.  // 运行时电梯不能开门。
                break;
            case STOPPING_STATE:
                // 电梯停了,可以开门了。
                System.out.println("电梯开门。");
                this.setState(OPENING_STATE);
                break;
            default:
                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;
            default:
                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;
            default:
                break;
        }
    }

}

package com.geek.state.patern.before;

/**
 * @author geek
 */
public class Client {

    public static void main(String[] args) {
        Lift lift = new Lift();
        // 电梯是停止的。
        lift.setState(ILift.STOPPING_STATE);
        // 开门。
        lift.open();
        // 关门。
        lift.close();
        // 运行。
        lift.run();
        // 停止。
        lift.stop();
    }

}

/*
Connected to the target VM, address: '127.0.0.1:53228', transport: 'socket'
电梯开门。
电梯关门。
电梯开始运行。
电梯停止了。
Disconnected from the target VM, address: '127.0.0.1:53228', transport: 'socket'

Process finished with exit code 0
 */

问题分析。

  • 使用了大量的 switch…case 这样的判断(if…else 也是一样),使程序的可阅读性变差。

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

定义。

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



结构。

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

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

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

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



案例实现。

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

在这里插入图片描述
代码如下。

package com.geek.state.patern.after;

/**
 * 抽象状态类。
 *
 * @author geek
 */
public abstract class AbstractClassLiftState {

    /**
     * 定义一个环境角色,也就是封装状态的变化引起的功能变化。
     */
    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();

}

package com.geek.state.patern.after;

/**
 * 环境角色。
 *
 * @author geek
 */
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 AbstractClassLiftState liftState;

    public AbstractClassLiftState getLiftState() {
        return this.liftState;
    }

    public void setLiftState(AbstractClassLiftState liftState) {
        // 当前环境改变。
        this.liftState = liftState;
        // 把当前的环境通知到各个实现类中。
        this.liftState.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.geek.state.patern.after;

/**
 * 电梯开启状态。
 *
 * @author geek
 */
public class OpeningState extends AbstractClassLiftState {

    /**
     * 电梯开门动作。
     */
    @Override
    public void open() {
        // 开启当然可以关闭了,我就想测试一下电梯门开关功能。
        System.out.println("电梯门开启。");
    }

    /**
     * 电梯关门动作。
     */
    @Override
    public void close() {
        // 状态修改。
        super.context.setLiftState(Context.CLOSING_STATE);
        // 动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作。
        super.context.getLiftState().close();
    }

    /**
     * 电梯运行动作。
     */
    @Override
    public void run() {
        // do nothing.  // 电梯门不能开着就跑,这里什么也不做。
    }

    /**
     * 电梯停止动作。
     */
    @Override
    public void stop() {
        // do nothing.  // 开门状态已经是停止的了。
    }

}

package com.geek.state.patern.after;

/**
 * 电梯关闭状态。
 *
 * @author geek
 */
public class ClosingState extends AbstractClassLiftState {

    /**
     * 电梯关门动作。
     */
    @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();
    }

}

package com.geek.state.patern.after;

/**
 * 电梯运行状态。
 *
 * @author geek
 */
public class RunningState extends AbstractClassLiftState {

    /**
     * 电梯开门动作。
     */
    @Override
    public void open() {
        // do nothing.  // 运行的时候开电梯门?你疯了!电梯不会给你开的。
    }

    /**
     * 电梯关门动作。
     */
    @Override
    public void close() {
        // do nothing.  // 电梯门关闭?这是肯定了。虽然可以关门,但这个动作不归我执行。
    }

    /**
     * 电梯运行动作。
     */
    @Override
    public void run() {
        // 这是在运行状态下要实现的方法。
        System.out.println("电梯正在运行...");
    }

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

}

package com.geek.state.patern.after;

/**
 * 电梯停止状态。
 *
 * @author geek
 */
public class StoppingState extends AbstractClassLiftState {

    /**
     * 电梯开门动作。
     */
    @Override
    public void open() {
        // 停止状态,开门,那是要的!
        // 状态修改。
        super.context.setLiftState(Context.OPENING_STATE);
        // 动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作。
        super.context.getLiftState().open();
    }

    /**
     * 电梯关门动作。
     */
    @Override
    public void close() {
        // 虽然可以关门,但这个动作不归我执行。
        // 状态修改。
        super.context.setLiftState(Context.CLOSING_STATE);
        // 动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作。
        super.context.getLiftState().close();
    }

    @Override
    public void run() {
        // 停止状态再跑起来,正常的很。
        // 状态修改。
        super.context.setLiftState(Context.RUNNING_STATE);
        // 动作委托为 CloseState 来执行,也就是委托给了 ClosingState 子类执行这个动作。
        super.context.getLiftState().run();
    }


    @Override
    public void stop() {
        // 停止状态是怎么发生的呢?当然是停止方法执行了。
        System.out.println("电梯停止。");
    }

}

package com.geek.state.patern.after;

/**
 * @author geek
 */
public class Client {

    public static void main(String[] args) {
        Context context = new Context();
        context.setLiftState(new CloseingState());

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

}



优缺点。

优点。

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

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

缺点。

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

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

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



使用场景。

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

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
状态模式State Pattern)是一种行为设计模式,它允许对象在其内部状态发生改变时改变它的行为。该模式的核心思想是封装对象的状态,使得它对外部的影响最小化。状态模式常常被应用于需要按照状态决定行为的场合,比如状态机、游戏状态等。 在状态模式中,我们通常会定义一个抽象状态类(Abstract State)和若干个具体状态类(Concrete State),每个具体状态类表示对象在不同状态下的行为。同时,我们还需要定义一个环境类(Context),它包含了一个状态对象,负责状态的切换和行为的调用。 状态模式的优点包括: 1. 状态模式状态的处理分散在不同的状态类中,使得每个状态类只需处理自己状态下的行为,降低了代码的复杂度和耦合度。 2. 状态模式状态行为分离,使得对象的状态可以在运行时动态改变,而不需要改变对象自身的结构。 3. 状态模式符合“开闭原则”,容易扩展和增加新的状态行为。 但是,状态模式也存在一些缺点,包括: 1. 状态模式会导致类的数量增加,其中每个状态都需要一个具体状态类,这可能会导致类的爆炸。 2. 状态模式会增加代码的复杂度,尤其是当状态之间的转换比较复杂时。 总之,状态模式是一种非常有用的设计模式,它可以让对象状态的变化更加灵活和可控。但是,在实际应用过程中,我们需要权衡其优缺点,选择合适的使用场景和实现方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lyfGeek

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

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

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

打赏作者

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

抵扣说明:

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

余额充值