自动贩卖机公司的需求
小明的公司在他们当地越来越有名,有很多公司都找到了他们,最近又有一家制作自动贩卖机的公司找到了他们,他们公司是做自动贩卖机的,他们需要将他们的售卖过程用程序实现,所以他们找到了小明的公司。
自动贩卖机公司递交了一份关于贩卖机售卖过程的文档给小明他们:
小明拿到这张图一看,觉得整个需求和过程都非常清晰,椭圆表示售卖机不同的状态,横线上面的动作表示售卖机的动作。于是开始写代码了,很快完成了售卖机。
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("已经将钱退出");
}
}
其他三种状态的实现相信大家已经很清楚了,在这里就不在赘述了。这样一来,我们的售卖机程序就非常清晰了,也利于我们的扩展或修改,如果要加入一个状态,只需要修改和这个状态相关的代码,而不会影响其他状态,这就是我们的状态模式。
定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
角色
- 上下文环境
- 抽象状态
- 具体状态
它与策略模式的区别
首相我们看看策略模式的类图:
惊人的相似!那么,他们是不是同一种模式呢,根据以往的经验,一般这种情况的两种模式一定是有区别的,不然它们不应该被分别定义,对于策略模式和状态模式的区别又是怎样的呢?其实两种模式最大的区别就是它们各自的意图,策略模式的上下文会选择不同的策略以表示该类不同的表现,类的表现从初始化后已经决定。而状态模式会决定上下文的状态和在不同状态下的表现,你某个时刻的体现取决于当前的状态对象,而下一个状态取决于当前状态对当前动作的结果,状态模式中会体现动作和改变,而策略模式中只体现了动作,只是动作被封装成了其他类,由其他类管理起来了。当两者有共同的作用就是解耦
,把类的可变部分由独立出来的类实现,不同的策略或者状态会体现出不同的动作。当然我们也发现,对于状态模式,不同的状态我们会实现不同状态接口,所以有多少状态,就会有多少的状态类,这样我们类的数量是必定会增加的,这是它的缺点。不过,如果我们类的状态过多,而且状态的切换非常复杂的话,那么我们的状态模式的优点是显而易见的。