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

2019-02-21  本文已影响0人  LY丶Smile

目的

阿里规约对于控制语句的推荐是尽量少用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;
    }
}

解析

四个状态类:

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

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

上一篇 下一篇

猜你喜欢

热点阅读