首页 >> 大全

1.3 使用状态机实现

2023-08-12 大全 32 作者:考证青年

接下来就实现(有25分钱):

package designMode.statePattern.gumballState;//import java.util.Random;//状态类:有币
//表示 糖果机里面接受了一个25分的硬币,在这个状态下,可以接收的动作:退币+转动曲柄
public class HasQuarterState implements State {GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert another quarter");}//执行退币,将糖果机的状态修改为 “无币”@Overridepublic void ejectQuarter() {System.out.println("Quarter returned");gumballMachine.setState(gumballMachine.getNoQuarterState());}//动作:扭动曲柄@Overridepublic void turnCrank() {System.out.println("You turned...");//根据状态图转换,扭动之后,将糖果机状态修改为 “售出糖果”gumballMachine.setState(gumballMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}@Overridepublic void refill() { }@Overridepublic String toString() {return "waiting for turn of crank";}
}

(售出糖果)类:

public class SoldState implements State {GumballMachine gumballMachine;public SoldState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("Please wait, we're already giving you a gumball");}@Overridepublic void ejectQuarter() {System.out.println("Sorry, you already turned the crank");}@Overridepublic void turnCrank() {System.out.println("Turning twice doesn't get you another gumball!");}//真正的执行动作在这里@Overridepublic void dispense() {//该状态调用糖果机来发放糖果//gumballMachine.releaseBall()用于对糖果机中糖果数量count进行改变--辅助方法gumballMachine.releaseBall();if (gumballMachine.getCount() > 0) {//判断状态来切换状态,先发再查,肯定不会虚发gumballMachine.setState(gumballMachine.getNoQuarterState());} else {System.out.println("Oops, out of gumballs!");gumballMachine.setState(gumballMachine.getSoldOutState());}}@Overridepublic void refill() { }@Overridepublic String toString() {return "dispensing a gumball";}
}

2.7 状态改变分析

糖果机持有每个状态类的实例,机器的当前状态总是这些实例之一。

状态转换:

当动作被调用时,它就会被委托到当前的状态。

从 状态 转变为 Sold状态:

代码中的状态切换:

3.状态模式 3.1 定义状态模式

状态模式定义:

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

从上面的状态改变分析就可以知道,转动曲柄的动作,将糖果机从“有2 5分钱”状态改变到“售卖糖果”状态。

这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随着内部状态而改变。

定义中的第二部分“对象看起来好像修改了它的类”,从客户角度来看:如果说你是要的对象能够完全改变它的行为,那么你就会觉得,这个对象实际上是从别的类实例化而来的。然而,实际上,你知道我们是在使用组合通过简单引用不同的状态对象来造成类改变的假象。

3.2 状态模式的类图

状态机实现使用1.3模式_状态机编写_

这个类图里面的内容全是重点:

(上下文)是一个类,它可以拥有一些内部状态。在我们的例子中,就是这个。

State接口定义了一个所有具体状态的共同接口;任何状态都实现这个相同的接口,这样一来,状态之间可以相互替换。

(具体状态)处理来自的请求。每一个都提供了它自己对于请求的实现。所以,当改变状态时,行为也跟着改变。

3.3 类图与策略模式相同

以状态模式而言,我们将一群行为封装在状态对象中,的行为随时可委托到那些状态对象中的一个。随着时间的流式,当前状态在状态对象集合中游走改变,以反映内部的状态,因此,的行为也会跟着改变。但是的客户对于状态对象了解不多,甚至根本是浑然不觉。

而以策略模式而言,客户通常主动指定所要组合的策略对象是哪一个。现在,固然策略模式让我们具有弹性,能够在运行时改变策略,但对于某个对象来说,通常都只有一个最适当的策略对象。比方说,在第1章,有些鸭子(例如绿头鸭)被设置成利用典型的飞翔行为进行飞翔,而有些鸭子(例如橡皮鸭和诱饵鸭)使用的飞翔行为只能让他们紧贴地面。

一般来说,我们把策略模式想成是除了继承之外的一种弹性替代方案。如果你使用继承定义了一个类的行为,你将被这个行为困住,甚至要修改它都很难。有了策略模式,你可以通过组合不同的对象来改变行为。

我们把状态模式想成是不用在中放置许多条件判断的替代方案。通过将行为包装进状态对象中,你可以通过在内简单地改变状态对象来改变的行为。

3.4 状态模式问答

问题1:在中,状态决定了下一个状态应该是什么。总是决定接下来的状态是什么吗?

回答1:不,并非总是如此。也可以决定状态转换的流向。一般来讲,当状态转换是固定的时候,就是和放在中;然而,当状态转换是更动态的时候,通常就会放在状态类中。例如,在中,由运行时糖果的数目来决定状态要转换到还是.

/**SoldState**///真正的执行动作在这里@Overridepublic void dispense() {//该状态调用糖果机来发放糖果//gumballMachine.releaseBall()用于对糖果机中糖果数量count进行改变--辅助方法gumballMachine.releaseBall();if (gumballMachine.getCount() > 0) {//判断状态来切换状态,先发再查,肯定不会虚发gumballMachine.setState(gumballMachine.getNoQuarterState());} else {System.out.println("Oops, out of gumballs!");gumballMachine.setState(gumballMachine.getSoldOutState());}}

在我们的实现汇总,我们试图通过使用上的方法把依赖减到最小,而不是显示硬编码具体状态类。

问题2:客户会直接和状态交互吗?

回答2:不会,状态是永存在中来代表他的内部状态以及行为的,所以只有才会对状态提出请求。客户不会直接改变的状态。全盘了解状态是的工作,客户根本不了解,所以不会直接和状态联系。

问题3:如果在程序中有许多实例,这些实例之间可以共享状态对象吗?

回答3:是可以的,这也是十分常见的做法。但唯一的前提是,状态对象不能持有他们自己的内部状态;否则就不能共享。 想要共享状态,需要把每个状态都指定到讲台的实例变量中。如果你的状态需要利用到中的方法或者实例变量,你还必须在每个()方法内传入一个的引用。

问题4:使用状态模式似乎总是增加我们设计类中的数目。请看的例子,新版本比旧版本多出了许多类。

回答4:没错,在个别的状态类中封装状态行为,结果总是增加这个设计中类的数目。这就是为了要获取弹性而付出的代价。除非你的代码是一次性的,可以用完就扔掉(是呀!才怪! ),那么其实状态模式的设计是绝对值得的。其实真正重要的是你暴露给客户的类数目,而且我们有办法将这些额外的状态类全都隐藏起来。

_状态机编写_状态机实现使用1.3模式

让我们看一下另一种做法:如果你有一个应用,它有很多状态,但是你决定不将这些状态封装在不同的对象中,那么你就会得到巨大的、整块的条件语句。这会让你的代码不客易维护和理解。通过使用许多对象,你可以让状态变得很干净,在以后理解和维护它们时,就可以省下很多的工夫。

问题5:状态模式类图显示State是一个抽象类,但你不是使用接口实现糖果机状态的吗?

回答5:如果我们没有共同的功能可以放进抽象类中,就会使用接口。在你实现状态模式时,很可能想使用抽象类。这么一来,当你以后需要在抽象类中加入新的方法时就很容易,不需妥打破具体状态的实现。

4.实现抽奖需求 4.1 中奖需求实现

需求在上面已经介绍了,就是需要新增一个中奖状态。因为已经实现了状态模式,实现新状态还是很简单的。

首先,先在中添加:

public class GumballMachine {State soldOutState;State noQuarterState;State hasQuarterState;State soldState;State winnerState;//新增加的状态类//...一些方法}

现在再来实现类,这个类也实现了State接口:

package designMode.statePattern.gumballStateWinner;public class WinnerState implements State {GumballMachine gumballMachine;public WinnerState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("Please wait, we're already giving you a Gumball");}@Overridepublic void ejectQuarter() {System.out.println("Please wait, we're already giving you a Gumball");}@Overridepublic void turnCrank() {System.out.println("Turning again doesn't get you another gumball!");}@Overridepublic void dispense() {gumballMachine.releaseBall();//首先发一个糖果if (gumballMachine.getCount() == 0) {gumballMachine.setState(gumballMachine.getSoldOutState());} else {gumballMachine.releaseBall();//如果还有第二个糖果的话,就再发第二个糖果System.out.println("YOU'RE A WINNER! You got two gumballs for your quarter");if (gumballMachine.getCount() > 0) {gumballMachine.setState(gumballMachine.getNoQuarterState());} else {System.out.println("Oops, out of gumballs!");gumballMachine.setState(gumballMachine.getSoldOutState());}}}@Overridepublic void refill() { }@Overridepublic String toString() {return "despensing two gumballs for your quarter, because YOU'RE A WINNER!";}
}

4.2 完善游戏

还需要做一个改变:需要实现机会随机数,并且增加一个进入的状态转换。这两件事情都要加进,因为顾客会从这个状态中转动曲柄:

package designMode.statePattern.gumballStateWinner;import java.util.Random;public class HasQuarterState implements State {//首先,增加一个随机数产生器,产生10%赢的机会Random randomWinner = new Random(System.currentTimeMillis());GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert another quarter");}@Overridepublic void ejectQuarter() {System.out.println("Quarter returned");gumballMachine.setState(gumballMachine.getNoQuarterState());}@Overridepublic void turnCrank() {System.out.println("You turned...");int winner = randomWinner.nextInt(10);//根据随机数,决定这个顾客是否赢了if ((winner == 0) && (gumballMachine.getCount() > 1)) {//如果赢了,并且有足够的糖果的话,就进入WinnerState状态,否则就进入SlodStategumballMachine.setState(gumballMachine.getWinnerState());} else {gumballMachine.setState(gumballMachine.getSoldState());}}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}@Overridepublic void refill() { }public String toString() {return "waiting for turn of crank";}
}

4.3 完整的展示样例

测试代码:

package designMode.statePattern.gumballStateWinner;public class GumballMachineTestDrive {public static void main(String[] args) {GumballMachine gumballMachine = new GumballMachine(10);System.out.println(gumballMachine);//线程操作对象类gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();System.out.println(gumballMachine);gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();System.out.println(gumballMachine);}
}

输出:

Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 10 gumballs
Machine is waiting for quarterYou inserted a quarter
You turned...
A gumball comes rolling out the slot...
You inserted a quarter
You turned...
A gumball comes rolling out the slot...Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 8 gumballs
Machine is waiting for quarterYou inserted a quarter
You turned...
A gumball comes rolling out the slot...
A gumball comes rolling out the slot...
YOU'RE A WINNER! You got two gumballs for your quarter
You inserted a quarter
You turned...
A gumball comes rolling out the slot...Mighty Gumball, Inc.
Java-enabled Standing Gumball Model #2004
Inventory: 5 gumballs
Machine is waiting for quarter

问题:我们为什么需要?为什么不直接在中发放两颗糖果?

回答:这是一个好问题,这两个状态几乎一样,唯一的差别在于,状态会发放两颗糖果。你当然可以将发放两颗糖果的代码放在中,当然这么做有缺点,因为你等于是将两个状态用一个状态类来代表。这样做你牺牲了状态类的清晰易懂来减少一些冗余代码。

你也应该考虑到在前面的章节中所学到的原则:一个类,一个责任。将状态的责任放进状态中,你等于是让状态具有两个责任。那么促销方案结束之后或者赢家的机率改变之后,你又该怎么办呢?所以,这必须用你的智慧来做折衷。

5.总结 状态模式允许一个对象基于内部状态而拥有不同的行为。和程序状态机( PSM)不同,状态模式用类代表状态。会将行为委托给当前状态对象。通过将每个状态封装进一个类,我们把以后需要做的任何改变局部化了。状态模式和策略模式有相同的类图,但是它们的意图不同。策略模式通常会用行为或算法来配置类。状态模式允许随着状态的改变而改变行为。状态转换可以由State类或类控制。使用状态模式通常会导致设计中类的数目大量增加。状态类可以被多个实例共享。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了