设计模式-状态模式(State)

简介: 设计模式-状态模式(State)

设计模式-状态模式

允许对象在内部状态改变的时候改变它的行为,对象看起来好像修改了它的类。通俗地说就是把所有行为包装在不同的类状态对象里,每一个状态对象都是抽象状态类的一个子类。

认识状态模式

所谓对象的状态,通常指的就是对象实例的属性的值;而行为指的就是对象的功能,再具体点说,行为大多可以对应到方法上。

状态模式的功能就是分离状态的行为,通过维护状态的变化,来调用不同状态对应的不同功能。也就是说,状态和行为是相关联的,它们的关系可以描述为:状态决定行为。

由于状态是在运行期被改变的,因此行为也会在运行期根据状态的改变而改变。

涉及角色

  • 环境上下文(Context)角色:包含客户端所有感兴趣的功能状态,并且持有一个具体状态的实例,这个具体状态的实例就是当前环境对象的现有状态。
  • 抽象状态(State)角色:定义一个所有具体状态的共同接口,用来封装环境角色对象的一个特定状态的行为。
  • 具体状态(ConcreteState):处理来自 Context 的请求,每个具体状态类都实现了环境的一个状态对应的行为特性。

场景模拟

适合在将各种不同状态转换有不同行为的场景,避免一堆的if else。将功能委托到状态类中去,代码清晰降低耦合,同时易于拓展。

假设现在一个肥宅程序猿下班了,去自动售货机买「肥宅汽水」回去喝。自动售货机需要投币即可自动出售。

  1. 默认初始化是「售罄状态」,添加汽水进入售货机后变成「没有硬币」状态。
  2. 投入硬币则进入「有硬币」状态,判断是否有汽水,有则进入「售出状态」售出汽水,售出后还有剩余则恢复到「没有硬币」状态。否则将硬币退回,并且进入汽水「售罄状态」。

现在我们可以抽象一个售货机充当 Context 角色,投币行为是 请求入口,投币后售货机会发生很多状态转换。状态分别有:售罄、有硬币、无硬币、售出。

假如我们不用状态模式,那么就要写一堆判断条件。代码也不可拓展与维护。新增一种状态,要修改所有的代码。

所有的请求都会委托到对应的状态类,环境角色拥有所有的状态类。

代码实现

首先我们创建一个 MachineState 接口,所有的状态实现该接口。

package com.zero.headfirst.states;
/**
 * 售货机状态接口
 */
public interface MachineState {
    /**
     * 委托到不同具体实现类实现对应的行为
     */
    void handleRequest();
}

接着我们定义无硬币状态 NoCoinState 用于处理无硬币状态的行为。

package com.zero.headfirst.states;
/**
 * 没有硬币状态
 */
public class NoCoinState implements MachineState {
    private MachineContext machineContext;
    public NoCoinState(MachineContext machineContext) {
        this.machineContext = machineContext;
    }
    @Override
    public void handleRequest() {
        System.out.println("你没有投币,请先投币。");
    }
}

有硬币状态下的行为,也就是投币后触发的行为。同时将状态切换到销售状态,并委托到对应状态处理。

package com.zero.headfirst.states;
/**
 * 投入硬币后状态
 */
public class HasCoinState implements MachineState {
    private MachineContext machineContext;
    public HasCoinState(MachineContext machineContext) {
        this.machineContext = machineContext;
    }
    @Override
    public void handleRequest() {
        System.out.println("收到硬币,即将为你准备汽水,请稍等...");
        //进入销售状态
        machineContext.setCurrentState(machineContext.getSellingState());
        //委托到销售中状态行为
        machineContext.getCurrentState().handleRequest();
    }
}

售出货物状态,售出后判断是否售罄进入不同的状态。

  1. 若汽水数量 > 0,状态变成无硬币状态。
  2. 否则进入售罄状态,这样客户再投币就可以继续执行销售还是提示售罄退回硬币。
package com.zero.headfirst.states;
/**
 * 售出货物状态
 */
public class SellingState implements MachineState {
    private MachineContext machineContext;
    public SellingState(MachineContext machineContext) {
        this.machineContext = machineContext;
    }
    @Override
    public void handleRequest() {
        int count = machineContext.getCount();
        --count;
        machineContext.setCount(count);
        System.out.println("正在售出汽水,请取走。");
        if (count > 0) {
            //恢复到没有硬币状态
            machineContext.setCurrentState(machineContext.getNoCoinState());
        } else {
            //进入售罄状态
            machineContext.setCurrentState(machineContext.getSellOutState());
        }
    }
}

售罄状态行为

package com.zero.headfirst.states;
/**
 * 售罄状态
 */
public class SellOutState implements MachineState {
    private MachineContext machineContext;
    public SellOutState(MachineContext machineContext) {
        this.machineContext = machineContext;
    }
    @Override
    public void handleRequest() {
        System.out.println("汽水售罄,退回你的硬币");
    }
}

最后定义我们的自动售货机

package com.zero.headfirst.states;
/**
 * 机器上下文-持有所有状态,以及当前状态的引用
 */
public class MachineContext {
    /**
     * 持有所有状态
     */
    private MachineState noCoinState;
    private MachineState hasCoinState;
    private MachineState sellingState;
    private MachineState sellOutState;
    public MachineContext(int count) {
        noCoinState = new NoCoinState(this);
        hasCoinState = new HasCoinState(this);
        sellingState = new SellingState(this);
        sellOutState = new SellOutState(this);
        this.count = count;
        if (count > 0) {
            currentState = noCoinState;
        } else {
            currentState = sellOutState;
        }
    }
    /**
     * 默认售罄状态
     */
    private MachineState currentState = sellOutState;
    /**
     * 记录肥宅水数量
     */
    private int count;
    //所有的行为都委托到当前状态类
    /**
     * 投入硬币
     */
    public void putCoin() {
        if (count > 0) {
            this.setCurrentState(getHasCoinState());
        }
        currentState.handleRequest();
    }
    public MachineState getNoCoinState() {
        return noCoinState;
    }
    public MachineState getHasCoinState() {
        return hasCoinState;
    }
    public MachineState getSellingState() {
        return sellingState;
    }
    public MachineState getSellOutState() {
        return sellOutState;
    }
    public MachineState getCurrentState() {
        return currentState;
    }
    public void setCurrentState(MachineState currentState) {
        this.currentState = currentState;
    }
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
}

定义我们的客户端

package com.zero.headfirst.states;
/**
 * 客户端模拟投入硬币调用自动售货机提供的接口
 */
public class Client {
    public static void main(String[] args) {
        MachineContext machineContext = new MachineContext(2);
        // 模拟投入硬币
        machineContext.putCoin();
        machineContext.putCoin();
      //这里会提示售罄,退回硬币
        machineContext.putCoin();
        //模拟管理员添加汽水
        machineContext.setCount(1);
        machineContext.putCoin();
    }
}

测试结果

收到硬币,即将为你准备汽水,请稍等...
正在售出汽水,请取走。
收到硬币,即将为你准备汽水,请稍等...
正在售出汽水,请取走。
汽水售罄,退回你的硬币
收到硬币,即将为你准备汽水,请稍等...
正在售出汽水,请取走。

总结

从上面可以看出,环境类Context的行为request()是委派给某一个具体状态类的。通过使用多态性原则,

可以动态改变环境类Context的属性State的内容,使其从指向一个具体状态类变换到指向另一个具体状态类,

从而使环境类的行为request()由不同的具体状态类来执行。

策略模式与状态模式对比

  • 状态模式:不同的状态标表示不同的行为,对应不同的处理方式。
  • 策略模式:同一个行为,不同处理。因此在同一个行为发生的时候,可以根据条件挑选任意一个实现来进行相应的处理。

关于策略模式,读者可以阅读历史文章-策略模式


相关文章
|
6月前
|
设计模式
设计模式之 State(状态模式)
设计模式之 State(状态模式)
41 0
|
2月前
|
设计模式 Java 测试技术
Java设计模式-状态模式(18)
Java设计模式-状态模式(18)
|
3月前
|
设计模式 网络协议 Java
【十五】设计模式~~~行为型模式~~~状态模式(Java)
文章详细介绍了状态模式(State Pattern),这是一种对象行为型模式,用于处理对象在其内部状态改变时的行为变化。文中通过案例分析,如银行账户状态管理和屏幕放大镜工具,展示了状态模式的应用场景和设计方法。文章阐述了状态模式的动机、定义、结构、优点、缺点以及适用情况,并提供了Java代码实现和测试结果。状态模式通过将对象的状态和行为封装在独立的状态类中,提高了系统的可扩展性和可维护性。
【十五】设计模式~~~行为型模式~~~状态模式(Java)
|
4月前
|
设计模式 JavaScript Go
js设计模式【详解】—— 状态模式
js设计模式【详解】—— 状态模式
80 7
|
5月前
|
设计模式
状态模式-大话设计模式
状态模式-大话设计模式
|
5月前
|
设计模式 存储
行为设计模式之状态模式
行为设计模式之状态模式
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~状态模式
[设计模式 Go实现] 行为型~状态模式
|
6月前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
6月前
|
设计模式 Java
【设计模式系列笔记】状态模式
在Java中,状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的关键思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态的对象。这样,当对象的状态发生变化时,其行为也会相应地发生变化。
67 0
|
6月前
|
设计模式 Java
小谈设计模式(17)—状态模式
小谈设计模式(17)—状态模式