设计模式 —— 状态模式(State Pattern)

状态模式(State Pattern)

概念:状态模式 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类


在软件设计中,我们经常会遇需要编写有很多状态的程序。最简单的如乘坐电梯程序,当我们要坐电梯时需要判断电梯的状态,只有当电梯处于当前楼时我们才能乘坐,当电梯不在当前楼层时我们要按下按钮等待电梯到来。在平时一般都通过 if…else 或者 switch 判断状态后处理,这种固定的写法只有在软件后期不会更新时才可以(不过是不可能的),状态模式其实是使用组合通过简单引用不同的状态 对象 来造成状态改变的假象。


组成:

Context(上下文):一般是一个拥有多个状态的类,当调用 request() 函数时会被委托到状态对象。

State(状态接口):定义了所有具体状态的共同接口,任何状态都实现这个接口,这样就能在状态之间相互转换。

ConcreteStateA(具体状态):实现状态接口,处理来自 Context 的请求。每个 ConcreteState 都提供自己请求的实现。


例子:

我们实现电梯的例子,电梯的状态包括运行、停止、开门、关门。当电梯运行时我们等待电梯停止,停止时我们等待开门或者运行,开门时进入后关门,进入后电梯变为关门状态。

假设目前就这么多状态,用传统的 if…elseswitch 实现。

抽象状态类

public abstract class LiftState {
    //4种状态
    final int OPEN_STATE = 1;
    final int STOP_STATE = 2;
    final int CLOSE_STATE = 3;
    final int RUN_STATE = 4;

    public abstract void setState(int state);
    //开门动作
    public abstract void open();
    //关门动作
    public abstract void close();
    //运行动作
    public abstract void run();
    //停止动作
    public abstract void stop();
}

public class Lift extends LiftState{
    private int state;

    public Lift(int state) {
        this.state = state;
    }

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

    //开门动作
    @Override
    public void open() {
        switch (state) {
            case OPEN_STATE:
                System.out.println("处于开门状态:什么也不做");
                break;
            case CLOSE_STATE:
                System.out.println("处于关门状态:什么也不做");
                break;
            case RUN_STATE:
                System.out.println("处于运行状态:什么也不做");
                break;
            case STOP_STATE:
                System.out.println("处于关门状态,开门...");
                setState(OPEN_STATE);
                break;
        }
    }

    //关门动作
    @Override
    public void close() {
        switch (state) {
            case OPEN_STATE:
                System.out.println("处于开门状态:关门...");
                setState(CLOSE_STATE);
                break;
            case CLOSE_STATE:
                System.out.println("处于关门状态:什么也不做");
                break;
            case RUN_STATE:
                System.out.println("处于运行状态:什么也不做");
                break;
            case STOP_STATE:
                System.out.println("处于停止状态:什么也不做");
                break;
        }
    }

    //运行动作
    @Override
    public void run() {
        switch (state) {
            case OPEN_STATE:
                System.out.println("处于开门状态:什么也不做");
                break;
            case CLOSE_STATE:
                System.out.println("处于关门状态:运行...");
                setState(RUN_STATE);
                break;
            case RUN_STATE:
                System.out.println("处于运行状态:什么也不做");
                break;
            case STOP_STATE:
                System.out.println("处于停止状态:运行...");
                setState(RUN_STATE);
        }
    }

    //停止动作
    @Override
    public void stop() {
        switch (state) {
            case OPEN_STATE:
                System.out.println("处于开门状态:什么也不做");
                break;
            case CLOSE_STATE:
                System.out.println("处于关门状态:什么也不做");
                setState(CLOSE_STATE);
                break;
            case RUN_STATE:
                System.out.println("处于运行状态:停止...");
                break;
            case STOP_STATE:
                System.out.println("处于停止状态:什么也不做");
        }
    }
}

public class LiftRun {
    public static void main(String[] args) {
        //初始设置为停止状态
        Lift lift = new Lift(2);
        //停止变为运行
        lift.run();
        //运行时不能开门
        lift.open();
        //运行变为停止
        lift.stop();
        //停止时不能开门
        lift.close();
    }
}

上面的实现没什么问题,但大量的用了 switch…case,当我们想要为电梯增加一种状态时,会发现需要大量的改动代码,每一个 switch 都要改,这也是《重构》中称 switch代码的坏味道。我们来通过 状态模式 改变它。


状态模式改进例子:

LiftState:

public abstract class LiftState {
    Lift lift;

    public LiftState(Lift lift) {
        this.lift = lift;
    }

    //开门动作
    public abstract void open();
    //关门动作
    public abstract void close();
    //运行动作
    public abstract void run();
    //停止动作
    public abstract void stop();
}


CloseState:

public class CloseState extends LiftState {
    public CloseState(Lift lift) {
        super(lift);
    }

    @Override
    public void open() {
        System.out.println("处于关闭状态...什么也不做");
    }

    @Override
    public void close() {
        System.out.println("处于关闭状态...什么也不做");
    }

    @Override
    public void run() {
        System.out.println("处于关闭状态:运行");
        lift.setState(lift.getRunState());
    }

    @Override
    public void stop() {
        System.out.println("处于关闭状态...什么也不做");
    }
}


OpenState:

public class OpenState extends LiftState {
    public OpenState(Lift lift) {
        super(lift);
    }

    @Override
    public void open() {
        System.out.println("处于开门状态...什么也不错");
    }

    @Override
    public void close() {
        System.out.println("处于开门状态:关门");
        lift.setState(lift.getCloseState());
    }

    @Override
    public void run() {
        System.out.println("处于开门状态...什么也不错");
    }

    @Override
    public void stop() {
        System.out.println("处于开门状态...什么也不错");
    }
}


RunState:

public class RunState extends LiftState {
    public RunState(Lift lift) {
        super(lift);
    }

    @Override
    public void open() {
        System.out.println("处于运行状态...什么也不做");
    }

    @Override
    public void close() {
        System.out.println("处于运行状态...什么也不做");
    }

    @Override
    public void run() {
        System.out.println("处于运行状态...什么也不做");
    }

    @Override
    public void stop() {
        System.out.println("处于运行状态:停止");
        lift.setState(lift.getStopState());
    }
}


StopState:

public class StopState extends LiftState {
    public StopState(Lift lift) {
        super(lift);
    }

    @Override
    public void open() {
        System.out.println("处于停止状态:开门");
        lift.setState(lift.getOpenState());
    }

    @Override
    public void close() {
        System.out.println("处于停止状态...什么也不做");
    }

    @Override
    public void run() {
        System.out.println("处于停止状态:运行");
        lift.setState(lift.getRunState());
    }

    @Override
    public void stop() {
        System.out.println("处于停止状态...什么也不做");
    }
}


Lift:

public class Lift {
    LiftState openState;
    LiftState closeState;
    LiftState runState;
    LiftState stopState;

    LiftState state;

    public Lift() {
        openState = new OpenState(this);
        closeState = new CloseState(this);
        runState = new RunState(this);
        stopState = new StopState(this);
        //起始设置为停止状态
        state = stopState;
    }
    //委托给状态对象执行
    public void stop() {
        state.stop();
    }

    public void run() {
        state.run();
    }

    public void close() {
        state.close();
    }

    public void open() {
        state.open();
    }
    //仅仅更换当前对象的引用
    public void setState(LiftState state) {
        this.state = state;
    }

    public LiftState getOpenState() {
        return openState;
    }

    public LiftState getCloseState() {
        return closeState;
    }

    public LiftState getRunState() {
        return runState;
    }

    public LiftState getStopState() {
        return stopState;
    }
}


LiftRun:

public class LiftRun {
    public static void main(String[] args) {
        //其实设置为停止状态
        Lift lift = new Lift();
        lift.run();
        lift.open();
        lift.stop();
        lift.close();
    }
}

运行结果:

通过状态模式改进后,我们将每个状态和状态行为封装成了对象和方法,虽然增加了类的数目,但是当我们增加状态时不用改动原先的代码,仅需创建新的状态实现接口的方法即可。

如增加故障状态:

public class BugState extends LiftState {
    public BugState(Lift lift) {
        super(lift);
    }

    @Override
    public void open() {
        System.out.println("故障状态,不能打开");
    }

    @Override
    public void close() {
        System.out.println("故障状态,不能关闭");
    }

    @Override
    public void run() {
        System.out.println("故障状态,不能运行");
    }

    @Override
    public void stop() {
        System.out.println("故障状态,停止..维修");
        lift.setState(lift.getStopState());
    }

    @Override
    public void bug() {
        System.out.println("故障状态");
    }
}

在每个状态对象中加入相应的 bug 状态函数,如:

    @Override
    public void bug() {
        System.out.println("处于停止状态:维修电梯");
        lift.setState(lift.getBugState());
    }

接着在 Lift 类中加入 bug 状态:

    LiftState bugState;
    ...
    //构造函数中
    bugState = new BugState(this);
    ...
    public LiftState getBugState() {
        return bugState;
    }

测试函数:

public class LiftRun {
    public static void main(String[] args) {
        //其实设置为停止状态
        Lift lift = new Lift();
        //运行
        lift.run();
        lift.bug();
        lift.stop();
        lift.run();
    }
}

处于状态模式下仅仅需要新增一个类外加做一些小改动即可完成状态的增加,大大方便了后期的维护和修改。

适用场景:

  • 一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
  • 代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else 或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

优缺点:

优点:

  • 封装了转换规则,将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
时间: 2024-11-19 16:25:55

设计模式 —— 状态模式(State Pattern)的相关文章

设计模式 - 状态模式(state pattern) 未使用状态模式 详解

状态模式(state pattern) 未使用状态模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式可以控制状态的转换, 未使用设计模式时, 程序会非常繁杂. 具体方法: 1. 状态转换类. /** * @time 2014年7月11日 */ package state; /** * @author C.L.Wang * */ public class GumballMachine { final static int SOLD_OUT =

设计模式 - 状态模式(state pattern) 详解

状态模式(state pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 状态模式(state pattern): 允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类. 建立Context类, 包含多个具体状态(concrete state)类的组合, 根据状态的不同调用具体的方法, state.handle(), 包含set\get方法改变状态. 状态接口(state interface), 包含抽象方法handle(),

设计模式(行为型)之状态模式(State Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(行为型)之模板方法模式(Template Method Pattern)>http://blog.csdn.net/yanbober/article/details/45501715 概述 状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题.当系统中

[设计模式] 20 状态模式 State Pattern

在GOF的<设计模式:可复用面向对象软件的基础>一书中对状态模式是这样说的:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对于一个对象的状态,我们都是让这个对象包含一个状态的属性,这个状态属性记录着对象的具体状态,根据状态的不同使用分支结构来执行不同的功能,就像上面的代码那样处理:就像上面说的,类中存在大量的结构类似的分支语句,变得难以维护和理解.状态模式消除了分支语句,就像工厂模式消除了简单工厂模式的分支语句一样,将状态处理分散

状态模式(State Pattern)

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. 上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理. 抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为. 具体状态(Concrete State

深入浅出设计模式——状态模式(State Pattern)

模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化.在UML中可以使用状态图来描述对象状态的变化. 模式定义状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类.其别名为状态对象(Objects for Stat

设计模式之状态模式(State)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式 - 代理模式(proxy pattern) 未使用代理模式 详解

代理模式(proxy pattern) 未使用代理模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 部分代码参考: http://blog.csdn.net/caroline_wendy/article/details/37698747 如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供

设计模式 状态模式 以自动售货机为例

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/26350617 状态模式给了我眼前一亮的感觉啊,值得学习~ 先看定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类.定义又开始模糊了,理一下,当对象的内部状态改变时,它的行为跟随状态的改变而改变了,看起来好像重新初始化了一个类似的. 下面使用个例子来说明状态模式的用法,现在有个自动售货机的代码需要我们来写,状态图如下: 分析一个这个状态图: a.包含4个状态(

设计模式 - 命令模式(command pattern) 撤销(undo) 详解

命令模式(command pattern) 撤销(undo) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考命令模式: http://blog.csdn.net/caroline_wendy/article/details/31379977 命令模式可以用于执行撤销(undo)操作. 具体方法: 1. 对象类中需要保存状态, 如level. package command; public class CeilingFan { String loca