状态模式与策略模式
其实策略模式和状态模式是一对双胞胎,只是在出生的时候才分开了。
在设计随笔-状态模式我们已经了解到了策略模式的好处了,状态模式和策略模式相似的很高,但是状态模式注重的是通过改变对象内部的状态来帮助对象控制自己的行为。
需求和实现
现在我们(产品)要开始讲需求了,我们需要做一个java糖果机,以下是糖果机的工作流程,希望你们这帮程序员能够尽快的实现它:
我们可以在流程图中找出几个状态:有钱的状态、没钱的状态、售出糖果的状态和糖果售罄的状态。
于是我们创建对应的代码:
final static int SOLD_OUT = 0; //糖果售罄的状态
final static int NO_QUARTER = 1; //没有钱的状态
final static int HAS_QUARTER = 2; //有钱的状态
final static int SOLD = 3; //售出糖果的状态
int state = SOLD_OUT;
现在我们将每个状态变化的过程串联起来形成一个方法:
比如投入25分钱的方法
public void insertQuarter(){
if (state == HAS_QUARTER){
System.out.println("你已经投币了,不能再投了");
}else if (state == NO_QUARTER){
state = HAS_QUARTER;
System.out.println("投币成功");
}else if (state == SOLD_OUT){
System.out.println("该糖果机已经卖完了糖果了,我们拒绝投币");
}else if (state == SOLD){
System.out.println("请稍等,机器正在处理中。正在转换状态");
}
}
完整的一个java糖果机代码如下:
public class GumballMachine {
final static int SOLD_OUT = 0; //糖果售罄的状态
final static int NO_QUARTER = 1; //没有钱的状态
final static int HAS_QUARTER = 2; //有钱的状态
final static int SOLD = 3; //售出糖果的状态
int state = SOLD_OUT;
int count = 0;
public GumballMachine(int count) {
this.count = count;
if (count > 0){
state = NO_QUARTER;
}
}
/**
* 当有25分投进来的时候,执行该方法
*/
public void insertQuarter(){
if (state == HAS_QUARTER){
System.out.println("你已经投币了,不能再投了");
}else if (state == NO_QUARTER){
state = HAS_QUARTER;
System.out.println("投币成功");
}else if (state == SOLD_OUT){
System.out.println("该糖果机已经卖完了糖果了,我们拒绝投币");
}else if (state == SOLD){
System.out.println("请稍等,机器正在处理中。正在转换状态");
}
}
/**
* 退钱执行此方法
*/
public void ejectQuarter(){
if (state == HAS_QUARTER){
System.out.println("如果有25分钱,就把钱退出来,回来没有25分钱的状态");
state = NO_QUARTER;
}else if (state == NO_QUARTER){
System.out.println("你没有投币哦,所以不能退钱给你");
}else if (state == SOLD_OUT){
System.out.println("你已经转动了按钮,不能退钱了哦");
}else if (state == SOLD){
System.out.println("请稍等,机器正在处理中。正在转换状态");
}
}
/**
* 扭动按钮执行的方法
*/
public void turnCrank(){
if (state == HAS_QUARTER){
System.out.println("扭动中,给你一颗糖果了");
state = SOLD;
dispense();
}else if (state == NO_QUARTER){
System.out.println("你没有投币,不能扭出糖果哦");
}else if (state == SOLD_OUT){
System.out.println("我们不能给你糖果哦,因为以为已经售罄了");
}else if (state == SOLD){
System.out.println("你已经扭过了按钮了,不能再扭第二次来出糖果");
}
}
/**
* 调用此方法,发放糖果
*/
public void dispense(){
if (state == HAS_QUARTER){
System.out.println("不会有该操作,不执行");
}else if (state == NO_QUARTER){
System.out.println("不会有该操作,不执行");
}else if (state == SOLD_OUT){
System.out.println("不会有该操作,不执行");
}else if (state == SOLD){
System.out.println("正在售出糖果中");
count -= 1;
if (count == 0){
System.out.println("售罄");
state = SOLD_OUT;
}else {
state = NO_QUARTER;
}
}
}
}
更变的需求和状态模式的实现
现在我们要在更变的需求是在旋转按钮售出糖果的时候在进行抽奖,有10%的几率继续售出(奖励)一个糖果哦。
我们上面的代码实现的很详细,但是这个详细的代码并不容易实现扩展。于是我们加入了一个新的设计模式-状态模式来实现接下来的需求了。
首先定义状态 接口和类:
我们从NoQuarterState(没有钱的状态)开始实现吧
public class NoQuarterState implements State {
GumballMachine gumballMachine;
public NoQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("You inserted a quarter");
gumballMachine.insertQuarter();
}
@Override
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter");
}
@Override
public void turnCrank() {
System.out.println("You turned, but there's no quarter");
}
@Override
public void dispense() {
System.out.println("You need to pay first");
}
}
我们的java糖果机也变得焕然一新了。完整代码如下:
public class GumballMachine {
//把原先用int值代表的状态换成对象代表,然后在他们这些对象来回的切换,这就是状态模式的精髓所在了。
State soldOutState;
State noQuarterState;
State hasQuarterState;
State soldState;
State winnerState;
State state = soldOutState;
int count = 0;
public GumballMachine(int count) {
this.count = count;
if (count > 0){
//初始化为没有投币的状态
state = noQuarterState;
}
}
public void insertQuarter(){
state.insertQuarter();
}
public void ejectQuarter(){
state.ejectQuarter();
}
public void turnCrank(){
state.turnCrank();
}
void setState(State state){
this.state = state;
}
void releaseBall(){
System.out.println("A gumball comes rolling out the slot...");
if (count != 0){
count = count - 1;
}
}
}
更多的状态实现:
有钱的状态:
public class HasQuarterState implements State {
Random winnerRandom = new Random(System.currentTimeMillis());
GumballMachine gumballMachine;
public HasQuarterState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("You can't insert another quarter");
}
@Override
public void ejectQuarter() {
System.out.println("Quarter returned");
gumballMachine.setState(gumballMachine.noQuarterState);
}
@Override
public void turnCrank() {
System.out.println("You turned...");
int winner = winnerRandom.nextInt(10);
//10%的几率中奖,转成中奖的winnerState状态
if (winner==0 && gumballMachine.count > 1){
gumballMachine.setState(gumballMachine.winnerState);
}else{
gumballMachine.setState(gumballMachine.soldState);
}
}
@Override
public void dispense() {
System.out.println("No gumball dispensed");
}
}
售出糖果的状态:
public class SoldState implements State {
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("Please wait, we're already giving you a gumball");
}
@Override
public void ejectQuarter() {
System.out.println("Sorry, you already turned the crank");
}
@Override
public void turnCrank() {
System.out.println("Turning twice doesn't get you another gumball!");
}
@Override
public void dispense() {
//当售出糖果之后,根据剩余糖果数量判断转成没钱的状态还是售罄状态
gumballMachine.releaseBall();
if (gumballMachine.count > 0){
gumballMachine.setState(gumballMachine.noQuarterState);
}else{
System.out.println("no gumballs");
gumballMachine.setState(gumballMachine.soldOutState);
}
}
}
糖果售罄的状态:
public class SoldOutState implements State {
GumballMachine gumballMachine;
public SoldOutState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("no gumballs");
}
@Override
public void ejectQuarter() {
System.out.println("no gumballs");
}
@Override
public void turnCrank() {
System.out.println("no gumballs");
}
@Override
public void dispense() {
System.out.println("no gumballs");
}
}
抽中糖果的状态:
public class WinnerState implements State {
GumballMachine gumballMachine;
@Override
public void insertQuarter() {
System.out.println("error");
}
@Override
public void ejectQuarter() {
System.out.println("error");
}
@Override
public void turnCrank() {
System.out.println("error");
}
@Override
public void dispense() {
//判断count糖果数量,如果没有了则为售罄状态,有的话为没有钱的状态
System.out.println("you a winner ! you get a two gumball");
gumballMachine.releaseBall();
if (gumballMachine.count == 0){
gumballMachine.setState(gumballMachine.soldOutState);
}
else{
gumballMachine.releaseBall();
if (gumballMachine.count > 0){
gumballMachine.setState(gumballMachine.noQuarterState);
}else {
System.out.println("out of gumball");
gumballMachine.setState(gumballMachine.soldOutState);
}
}
}
}
状态模式就是:定义一些一个对象需要改变的状态对象,这些状态对象可以在运行时来回切换。
总结
状态模式:允许对象在内部改变时改变它的行为,对象看起来好像修改了它的类。