设计模式之状态模式

自动贩卖机公司的需求

小明的公司在他们当地越来越有名,有很多公司都找到了他们,最近又有一家制作自动贩卖机的公司找到了他们,他们公司是做自动贩卖机的,他们需要将他们的售卖过程用程序实现,所以他们找到了小明的公司。

自动贩卖机公司递交了一份关于贩卖机售卖过程的文档给小明他们:

VendingMachine

小明拿到这张图一看,觉得整个需求和过程都非常清晰,椭圆表示售卖机不同的状态,横线上面的动作表示售卖机的动作。于是开始写代码了,很快完成了售卖机。

public class VendingMachine {

    final static int FREE = 0;
    final static int HAS_MONEY= 1;
    final static int HAS_GOODS = 2;
    final static int SOLD = 3;
    final static int HAS_CHANGE = 4;

    int state = FREE;
    double money = 0;
    double change = 0;

    public VendingMachine(int count) {
    }

    public void insertMoney(double money) {
        if (state == FREE) {
            state = HAS_MONEY;
            System.out.println("你已经投入" + money + "元");
        } else if (state == HAS_MONEY) {
            System.out.println("你已经投入" + (this.money + money) + "元");
        } else if (state == HAS_GOODS) {
            System.out.println("不能再投入");
        } else if (state == SOLD) {
            System.out.println("不能再投入");
        }else if (state == HAS_CHANGE) {
            System.out.println("不能再投入");
        }
    }

    public void selectGoods() {
        if (state == FREE) {
            System.out.println("你还没有投入钱");
        } else if (state == HAS_MONEY) {
            if(商品价格 > money){
                System.out.println("商品价格大于你投入的钱");
            }else {
                state = HAS_GOODS;
                System.out.println("你已经选择商品");
            }
        } else if (state == HAS_GOODS) {
            if(商品价格 > money){
                System.out.println("商品价格大于你投入的钱");
            }else {
                state = HAS_GOODS;
                System.out.println("你已经选择商品");
            }
        } else if (state == SOLD) {
            System.out.println("不能再选择");
        }else if (state == HAS_CHANGE) {
            System.out.println("不能再选择");
        }
    } 

    public void clickConfirm() {
        if (state == FREE) {
            System.out.println("你还没有投入钱");
        } else if (state == HAS_MONEY) {
            System.out.println("你还没有选择商品");
        } else if (state == HAS_GOODS) {
            state = SOLD;
            System.out.println("售出");
        } else if (state == SOLD) {
            System.out.println("已经售出");
        }else if (state == HAS_CHANGE) {
            System.out.println("已经售出");
        }
    }

    public void shipment() {
        if (state == SOLD) {
            change = money - 商品价格;
            if (change > 0) {
                state = HAS_CHANGE;
            }else {
                state = FREE;
            }
        }else  {
            System.out.println("错误");
        }
    }

    public void giveChange() {
        if (state == FREE) {
            System.out.println("你还没有投入钱");
        } else if (state == HAS_MONEY) {
            state = FREE;
        } else if (state == HAS_GOODS) {
            System.out.println("不能找零");
        } else if (state == SOLD) {
            System.out.println("没有零钱");
        } else if (state == HAS_CHANGE) {
            state = FREE;
            System.out.println("找零");
        }
    }
}

代码很容易理解,在售卖机执行不同动作时,判断售卖机之前的状态,然后执行不同的动作,并且将售卖机更新为新的状态。小明很开心的把程序交给了主管,主管拿到项目并没有说什么,只是给小明说,你的程序没有问题,但是现在售卖机工厂增加了一个需求,在出货时随机(百分之一的概率)让当前购买用户可以中奖,小明这时候准备开始修改代码,但是发现,代码改动的非常大,不仅要加入新的状态,还要修改每个动作内的代码。主管发现了小明的犹豫说,我知道你已经发现了问题,你的代码对于之前的需求是没有问题的,但是之前说过,我们的程序是一定会变的,所以你的代码对于更新会出现很大的问题,违反了很多设计的原则,所以我们必须重构你的代码,而且是完全的重构,抛弃你现在的方式。我们先来分析一下售卖机厂的的需求:对于售卖机,有不同的状态,在不同的状态下发生不同动作时需要做不同的处理和更新,而且可能会加入新的状态和动作,所以状态对于售卖机是变化的部分,我们应该将它独立出来,那么针对这样的情况,我们使用状态模式就再适合不过了:

首先,定义一个状态接口:

public interface State {
    public void insertMoney(double money);
    public void selectGoods();
    public void clickConfirm();
    public void shipment();
    public void giveChange();
}

修改售卖机的代码:

public class VendingMachine {

    State freeState;
    State hasMoneyState;
    State hasGoodsState;
    State soldState;
    State hasChangeState;

    State status;

    double money;
    double change;

    public VendingMachine() {
        freeState = new FreeState(this);
        hasMoneyState = new HasMoneyState(this);
        hasGoodsState = new HasGoodsState(this);
        soldState = new SoldState(this);
        hasChangeState = new HasChangeState(this);
        state = freeState;
    }

    public void insertMoney(double money) {
        state.insertMoney(money);
    }

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

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

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

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

实现一个Free状态的实现类:

public class FreeState implements State {

    VendingMachine vendingMachine;

    public FreeState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    public void insertMoney(double money) {
        vendingMachine.money += money;
        vendingMachine.state = vendingMachine.hasMoneyState;
        System.out.println("你已经投入" + vendingMachine.money + "元");
    }

    public void selectGoods() {
        System.out.println("你还没有投入钱");
    } 

    public void clickConfirm() {
        System.out.println("你还没有投入钱");
    }

    public void shipment() {
        System.out.println("你还没有投入钱");
    }

    public void giveChange() {
        System.out.println("你还没有投入钱");
    }
}

实现已投入钱状态的实现类:

public class HasMoneyState implements State {

    VendingMachine vendingMachine;

    public HasMoneyState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }

    public void insertMoney(double money) {
        vendingMachine.money += money;
        System.out.println("你已经投入" + vendingMachine.money + "元");
    }

    public void selectGoods() {
        vendingMachine.state = vendingMachine.hasGoodsState;
        System.out.println("你已经选择商品");
    } 

    public void clickConfirm() {
        System.out.println("你还没有选择商品");
    }

    public void shipment() {
        System.out.println("你还没有选择商品");
    }

    public void giveChange() {
        vendingMachine.state = vendingMachine. freeState;
        System.out.println("已经将钱退出");
    }
}

其他三种状态的实现相信大家已经很清楚了,在这里就不在赘述了。这样一来,我们的售卖机程序就非常清晰了,也利于我们的扩展或修改,如果要加入一个状态,只需要修改和这个状态相关的代码,而不会影响其他状态,这就是我们的状态模式。

定义

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

状态模式

角色

  • 上下文环境
  • 抽象状态
  • 具体状态

它与策略模式的区别

首相我们看看策略模式的类图:
策略模式

惊人的相似!那么,他们是不是同一种模式呢,根据以往的经验,一般这种情况的两种模式一定是有区别的,不然它们不应该被分别定义,对于策略模式和状态模式的区别又是怎样的呢?其实两种模式最大的区别就是它们各自的意图,策略模式的上下文会选择不同的策略以表示该类不同的表现,类的表现从初始化后已经决定。而状态模式会决定上下文的状态和在不同状态下的表现,你某个时刻的体现取决于当前的状态对象,而下一个状态取决于当前状态对当前动作的结果,状态模式中会体现动作和改变,而策略模式中只体现了动作,只是动作被封装成了其他类,由其他类管理起来了。当两者有共同的作用就是解耦,把类的可变部分由独立出来的类实现,不同的策略或者状态会体现出不同的动作。当然我们也发现,对于状态模式,不同的状态我们会实现不同状态接口,所以有多少状态,就会有多少的状态类,这样我们类的数量是必定会增加的,这是它的缺点。不过,如果我们类的状态过多,而且状态的切换非常复杂的话,那么我们的状态模式的优点是显而易见的。

坚持原创分享,您的支持将鼓励我不断前行!