美文网首页
设计模式(七):《Head First设计模式》状态模式

设计模式(七):《Head First设计模式》状态模式

作者: LY丶Smile | 来源:发表于2019-02-21 18:22 被阅读0次

目的

阿里规约对于控制语句的推荐是尽量少用else,如果必须使用也不要超过三层,超过三层的就要考虑使用状态模式。

从阿里规约中可以很简单的看出来,状态模式中的每个状态其实就是一个if。只不过我们将一个状态的所有行为放在了一个类中。

用类代表了状态的好处是我们将以后需要变化的任何改变都局部化了。因为if-else代表着逻辑判断,越多层的else if,代表着越混乱,对于日后的代码阅读及维护就是灾难。

概念

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

Demo

01
需求分析

此Demo是书中的例子,主要是设计一个糖果机,糖果机上有4个功能(对应事件):投币、退币、转动手柄以获取糖果、发放糖果。其中发放糖果不属于用户操作行为,是糖果机内部动作。

每个行为事件都可能包含以下四种状态:

  • 未投币:不能进行任何操作
  • 已投币:可以转动手柄获取一颗糖果
  • 售卖状态:正在发放糖果,不能进行其他操作
  • 已售光:糖果机中无糖果,任何操作都不能进行

02
代码实现

状态接口,每个状态都包含以下几种行为

public interface State {
    
    // 投币
    void insertQuarter();

    // 退币
    void ejectQuarter();
    
    // 转动手柄以释放糖果
    void turnCrank();
    
    // 发放糖果
    void dispense();
}

未投币状态

public class NoQuarterState implements State {

    GumballMachine gumballMachine;
    
    public NoQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("已投币");
        // 投币后将状态转换为投币状态 HasQuarterState
        gumballMachine.setState(gumballMachine.getHasQuarterState());
    }

    @Override
    public void ejectQuarter() {
        System.out.println("小伙子,还没投币就想退钱吗?");
    }

    @Override
    public void turnCrank() {
        System.out.println("请投币~");
    }

    @Override
    public void dispense() {
        System.out.println("请投币~");
    }
}

投币状态

public class HasQuarterState implements State {

    GumballMachine gumballMachine;
    
    public HasQuarterState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("已投币,无法重复投币!");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("正在退币");
        // 退钱后状态变为无币状态
        gumballMachine.setState(gumballMachine.getNoQuarterState());
    }

    @Override
    public void turnCrank() {
        System.out.println("正在发放糖果,请稍后……");
        // 转动手柄后,进入售卖状态(只有售卖状态负责出货)
        gumballMachine.setState(gumballMachine.getSoldState());
    }

    @Override
    public void dispense() {
        System.out.println("此状态下不需要此行为");
    }
}

出货状态

public class SoldState implements State {

    GumballMachine gumballMachine;
    
    public SoldState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    
    @Override
    public void insertQuarter() {
        System.out.println("正在发放糖果,不允许投币");
    }

    @Override
    public void ejectQuarter() {
        System.out.println("正在发放糖果,无法退币");
    }

    @Override
    public void turnCrank() {
       //只允许转一次手柄,因为此状态一定是因为转动手柄进入的
        System.out.println("小伙子,你转再多次手柄也不会多给你一块糖的");
    }

    @Override
    public void dispense() {
        gumballMachine.releaseBall();
        // 如果还有剩余糖果,进入无币状态等待投币
        if(gumballMachine.getCount() > 0) {
            gumballMachine.setState(gumballMachine.getNoQuarterState());
        // 如果没有剩余糖果,则进入售罄状态
        } else {
            gumballMachine.setState(gumballMachine.getSoldOutState());
        }
    }
}

售罄状态

public class SoldOutState implements State {

    GumballMachine gumballMachine;
    
    public SoldOutState(GumballMachine gumballMachine) {
        this.gumballMachine = gumballMachine;
    }
    @Override
    public void insertQuarter() {
        System.out.println("糖果售罄");
    }

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

    @Override
    public void turnCrank() {
        System.out.println("糖果售罄");
    }

    @Override
    public void dispense() {
        System.out.println("糖果售罄");
    }
}

糖果机实现

public class GumballMachine {

    State noQuarterState;
    State hasQuarterState;
    State soldState;
    State soldOutState;
    
    // 当前状态
    State state = soldOutState;
    // 纪录糖果机内有多少糖果
    int count = 0;
    
    public GumballMachine(int number) {
        noQuarterState = new NoQuarterState(this);
    }
    
    public void insertQuarter() {
        state.insertQuarter();
    }
    
    public void ejectQuarter() {
        state.ejectQuarter();
    }
    
    public void turnCrank() {
        state.turnCrank();
        state.dispense();
    }
    
    // 发放糖果,并将糖果数量减1
    void releaseBall() {
        if(count != 0) {
            count -= 1;
        }
    }
    
    public State getNoQuarterState() {
        return noQuarterState;
    }

    public State getHasQuarterState() {
        return hasQuarterState;
    }

    public State getSoldState() {
        return soldState;
    }

    public State getSoldOutState() {
        return soldOutState;
    }

    public int getCount() {
        return count;
    }

    public State getState() {
        return state;
    }

    // 允许其他对象进行状态转换,此方法用于设置状态
    public void setState(State state) {
        this.state = state;
    }
}

解析

四个状态类:

  • NoQuarterState:未投币状态,需要提示用户投币,用户投币后转换状态
  • HasQuarterState:已投币状态,不能再次投币,可以转动手柄获取糖果
  • SoldState:售卖状态,正在发放糖果。发放完后需要检查剩余糖果数量转换状态
  • SoldOutState:售空状态,不能进行任何操作

从上述代码可以看出,状态模式让项目中的类数量变多了,但是这是值得的。设计模式的目的就是为了封装变化,让代码更清晰、易懂、易维护。

不应该牺牲类的清晰易懂而减少冗余代码

相关文章

  • 设计模式(七):《Head First设计模式》状态模式

    目的 阿里规约对于控制语句的推荐是尽量少用else,如果必须使用也不要超过三层,超过三层的就要考虑使用状态模式。 ...

  • 好书推荐

    1、主要讲23种设计模式《Head First设计模式》

  • 设计模式之state模式

    文章内容参考自《Head First设计模式》的状态模式这一节。 一、state模式 定义: 状态模式允许对象在内...

  • Swift设计模式-目录

    推荐图书:《Head First设计模式》《大话设计模式》《设计模式之禅》, 设计模式思维导图 图形说明一切: 设...

  • 策略模式(详解)

    策略模式(来自HeadFirst设计模式) 今天看了 Head First 设计模式的第一个模式,居然是策略模式,...

  • 设计模式02-观察者者设计模式

    [toc] 设计模式02-观察者者设计模式 主要来源Head First设计模式(书)观察者设计模式是JDK中使用...

  • 设计模式03-装饰者设计模式

    [toc] 设计模式03-装饰者设计模式 主要来源Head First设计模式(书) 第5个设计原则(开放-关闭原...

  • 2018-12-11

    head first html css word书籍 http权威指南 head first设计模式

  • 1.设计模式入门-策略模式

    《HEAD FIRST 设计模式》在第一章设计模式入门中介绍了策略模式(Strategy Pattern)。 定义...

  • 设计模式01-策略者设计模式

    @[toc] 策略者设计模式 主要来源Head First设计模式(书) 书中定义:策略者设计模式定义了算法族,分...

网友评论

      本文标题:设计模式(七):《Head First设计模式》状态模式

      本文链接:https://www.haomeiwen.com/subject/tciryqtx.html