【设计模式】23种设计模式之状态模式

状态模式

定义

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

状态模式和策略模式的区别:

策略模式允许在运行中使用不同的算法/策略,是在外部(客户主动使用)将具体的策略类传入;

状态模式允许在运行时,通过改变类的内部状态的方式,改变其行为。状态的改变是在类内部实现的,客户并不感知。

UML类图

在这里插入图片描述

使用场景

1.行为随状态改变而改变的场景。

2.条件、分支语句的代替者。

实例

例子:一个饮料机,有5类状态:售罄、已投入硬币、没有投入硬币、正在售出、赢家(一枚硬币换2瓶饮料)

饮料机

// 饮料机
public class DrinkMachine {
    // 售罄状态
    State soldOutState;
    // 没有硬币状态
    State noQuarterState;
    // 有硬币状态
    State hasQuarterState;
    // 售出状态
    State soldState;
    // 赢家状态
    State winnerState;
    // 当前状态
    State currentState = soldOutState;
    // 饮料机中饮料个数
    int drinkNumber = 0;

    public DrinkMachine(int drinkNumber) {
        this.drinkNumber = drinkNumber;
        soldState = new SoldState(this);
        noQuarterState = new NoQuarterState(this);
        hasQuarterState = new HasQuarterState(this);
        soldState = new SoldState(this);
        winnerState = new WinnerState(this);
        if (drinkNumber > 0) {
            // 饮料数量大于0,当前状态变为“没有投入硬币”状态
            currentState = noQuarterState;
        }
    }

    public void setState(State state) {
        this.currentState = state;
    }

    // 投入硬币
    public void insertQuarter() {
        currentState.insertQuarter();
    }

    // 取出硬币
    public void ejectQuarter() {
        currentState.ejectQuarter();
    }

    // 按下按钮
    public void pushTheButton() {
        currentState.PushTheButton();
    }

    // 释放饮料
    public void outDrink() {
        currentState.outDrink();
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getCurrentState() {
        return currentState;
    }

    public int getDrinkNumber() {
        return drinkNumber;
    }

    public State getWinnerState() {
        return winnerState;
    }

    public void setDrinkNumber(int drinkNumber) {
        this.drinkNumber = drinkNumber;
    }

    @Override
    public String toString() {
        return "DrinkMachine{" +
                "soldOutState=" + soldOutState +
                ", noQuarterState=" + noQuarterState +
                ", hasQuarterState=" + hasQuarterState +
                ", soldState=" + soldState +
                ", currentState=" + currentState +
                ", drinkNumber=" + drinkNumber +
                '}';
    }
}

状态接口

// 状态接口中定义了饮料机所有可以‘改变状态‘的动作
public interface State {

    // 投入25分硬币
    void insertQuarter();

    // 退出25分硬币
    void ejectQuarter();

    // 按下按钮
    void PushTheButton();

    // 发放饮料
    void outDrink();

}
// 饮料机处于没有硬币的状态
public class NoQuarterState implements State{
    DrinkMachine drinkMachine;

    public NoQuarterState(DrinkMachine drinkMachine){
        this.drinkMachine = drinkMachine;
    }

    // 投入25分硬币
    @Override
    public void insertQuarter() {
        System.out.println("你投入了一枚硬币...");
        // 改变饮料机状态为”有一枚硬币“状态
        drinkMachine.setState(drinkMachine.getHasQuarterState());
    }

    @Override
    public void ejectQuarter() {
        System.out.println("未投入硬币...");
    }

    @Override
    public void PushTheButton() {
        System.out.println("未投入硬币...");
    }

    @Override
    public void outDrink() {
        System.out.println("未投入硬币...");
    }
}
// 饮料机处于有硬币的状态
public class HasQuarterState implements State {
    DrinkMachine drinkMachine;

    // 赢家随机数
    Random winnerRandom = new Random(System.currentTimeMillis());

    public HasQuarterState(DrinkMachine drinkMachine) {
        this.drinkMachine = drinkMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("饮料机中已经存在硬币,不允许再次投入硬币...");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("正在取出硬币...");
        // 改变饮料机状态为”没有投入硬币“
        drinkMachine.setState(drinkMachine.getNoQuarterState());
    }

    @Override
    public void PushTheButton() {
        System.out.println("正在按下饮料机按钮...");
        if (winnerRandom.nextInt(2) == 0) {
            // 进入赢家模式
            // 可售出2瓶饮料
            drinkMachine.setState(drinkMachine.getWinnerState());
        } else {
            // 普通模式
            // 改变饮料机状态为”正在售出“
            drinkMachine.setState(drinkMachine.getSoldState());
        }
    }

    @Override
    public void outDrink() {
        System.out.println("请按下饮料机按钮...");
    }
}
// 饮料机处于正在售出状态
public class SoldState implements State {

    DrinkMachine drinkMachine;

    public SoldState(DrinkMachine drinkMachine) {
        this.drinkMachine = drinkMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("饮料机正在售出饮料,不允许投币...");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("饮料机正在售出饮料,不允许取币...");
    }

    @Override
    public void PushTheButton() {
        System.out.println("饮料机正在售出饮料,不允许按下按钮...");
    }

    @Override
    public void outDrink() {
        if (drinkMachine.getDrinkNumber() > 0) {
            // 饮料数量 > 0
            System.out.println("正在售出饮料...");
            drinkMachine.setDrinkNumber(drinkMachine.getDrinkNumber() - 1);
            drinkMachine.setState(drinkMachine.getNoQuarterState());
        } else {
            System.out.println("饮料机中饮料已经售罄...");
            drinkMachine.setState(drinkMachine.getSoldOutState());
        }
    }
}
// “赢家”状态(在“有一个硬币”状态时按下按钮后才能进入赢家状态)
// “赢家”状态下,可以发放两瓶饮料
public class WinnerState implements State {
    DrinkMachine drinkMachine;

    public WinnerState(DrinkMachine drinkMachine) {
        this.drinkMachine = drinkMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("当前不可投币...");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("当前不可取币...");
    }

    @Override
    public void PushTheButton() {
        System.out.println("当前不可按下按钮...");
    }

    @Override
    public void outDrink() {
        // 饮料机当前饮料数量
        int currentDrinkNum = drinkMachine.getDrinkNumber();
        if (currentDrinkNum >= 2) {
            // 满足赢家状态所需要的两瓶饮料时
            System.out.println("赢家模式:正在发放2瓶饮料...");
            drinkMachine.setDrinkNumber(currentDrinkNum - 2);
            drinkMachine.setState(drinkMachine.getNoQuarterState());
        } else {
            // 不满足,则进入普通的售出状态
            drinkMachine.setState(drinkMachine.getSoldState());
            drinkMachine.outDrink();
        }
    }
}

// 饮料机处于售罄状态
public class SoldOutState implements State{
    DrinkMachine drinkMachine;

    public SoldOutState(DrinkMachine drinkMachine){
        this.drinkMachine = drinkMachine;
    }

    @Override
    public void insertQuarter() {
        System.out.println("饮料机中没有饮料...");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("饮料机中没有饮料...");
    }

    @Override
    public void PushTheButton() {
        System.out.println("饮料机中没有饮料...");
    }

    @Override
    public void outDrink() {
        System.out.println("饮料机中没有饮料...");
    }
}

测试代码:

public class Test {
    public static void main(String[] args) {
        DrinkMachine drinkMachine = new DrinkMachine(20);
        System.out.println(drinkMachine);
        // 1.
        // 投币
        drinkMachine.insertQuarter();
        // 按下按钮
        drinkMachine.pushTheButton();
        // 滚出饮料
        drinkMachine.outDrink();
        System.out.println(drinkMachine);

        // 2.
        // 投币
        drinkMachine.insertQuarter();
        // 取币
        drinkMachine.ejectQuarter();
        System.out.println(drinkMachine);

        // 3.错误示范
        // 按下按钮
        drinkMachine.pushTheButton();
        System.out.println(drinkMachine);

        //4. 测试赢家模式
        System.out.println("测试赢家模式中...");
        for (int i = 0; i < 10; i++) {
            drinkMachine.insertQuarter();
            drinkMachine.pushTheButton();
            drinkMachine.outDrink();
        }

    }
}

优缺点

优点:

1.封装了转换规则。

2.枚举可能的状态,在枚举状态之前需要确定状态种类。

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

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

5.可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数

缺点:

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

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

3.状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

工作中的应用

暂无

设计原则工具箱

1.找出应用中可能需要变化的部分,把它们独立出来,不要和那些不需要变化的代码混合在一起

2.针对接口(超类型)编程而不是针对具体的实现编程

3.多用组合少用继承

4.为了交互对象之间的松耦合设计而努力

5.类应该对扩展开放,对修改关闭

6.依赖抽象,不要依赖具体类。(即不要让高层组件依赖低层组件,而且不管高层组件还是低层组件都应该依赖于抽象)

7.好莱坞原则:别调用我们,我们会调用你。(在好莱坞原则下,我们允许底层组件(具体实现类)将自己挂钩到系统上。但是由高层组件(抽象基类)决定什么时候和怎么去使用这些底层组件。换句话说,高层组件对待底层组件的方式是:“别调用我们,我们会调用你”)

8.单一职责原则:一个类应该只有一个引起变化的原因

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值