HeadFir st 设计模式学习笔记11——状态模式
1.我们这次的话题是要实现⼀个糖果机,这个糖果机中如下的⼀些状态以指导糖果机的转移:
我们根据这个状态机写了⼀个有i f . .el se为主体的状态机程序,对每⼀个状态的转入转出做了实现
:
pu bl i c cl ass Gu mbal l Mach i n e {
f i n al stat i c i n t SOLD_OUT = 0;
f i n al stat i c i n t NO_QUARTER = 1;
f i n al stat i c i n t HAS_QUARTER = 2;
f i n al stat i c i n t SOLD = 3;
i n t state = SOLD_OUT ;
i n t cou n t = 0;
pu bl i c Gu mbal l Mach i n e(i n t cou n t ) {
th i s. cou n t = cou n t ;
i f (cou n t > 0) {
state = NO_QUARTER;
}
}
pu bl i c v oi d i n sertQu arter() {
i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou can 't i n sert an oth er qu arter");
} el se i f (state == NO_QUARTER) {
state = HAS_QUARTER;
Sy stem.ou t .pri n t l n ("Y ou i n serted a qu arter");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("Y ou can 't i n sert a qu arter , th e mach i n e i s sol d ou t ");
} el se i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("Pl ease wai t , we're al ready gi v i n g y ou a gu mbal l ");
}
}
pu bl i c v oi d ej ectQu arter() {
i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("Qu arter retu rn ed");
state = NO_QUARTER;
} el se i f (state == NO_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou h av en 't i n serted a qu arter");
} el se i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("Sorry , y ou al ready tu rn ed th e cran k");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("Y ou can 't ej ect , y ou h av en 't i n serted a qu arter y et ");
}
}
pu bl i c v oi d tu rn Cran k() {
i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("T u rn i n g twi ce doesn 't get y ou an oth er gu mbal l ! ");
} el se i f (state == NO_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed bu t th ere's n o qu arter");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed, bu t th ere are n o gu mbal l s");
} el se i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed. . . ");
state = SOLD;
di spen se();
}
}
pu bl i c v oi d di spen se() {
i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("A gu mbal l comes rol l i n g ou t th e sl ot ");
cou n t = cou n t - 1;
i f (cou n t == 0) {
Sy stem.ou t .pri n t l n ("Oops, ou t of gu mbal l s! ");
state = SOLD_OUT ;
} el se {
state = NO_QUARTER;
}
} el se i f (state == NO_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou n eed to pay f i rst ");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("No gu mbal l di spen sed");
} el se i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("No gu mbal l di spen sed");
}
}
pu bl i c v oi d ref i l l (i n t n u mGu mBal l s) {
th i s. cou n t = n u mGu mBal l s;
state = NO_QUARTER;
}
pu bl i c St ri n g toSt ri n g() {
}
}
但是现在这个公司有了新的要求:当曲柄被转动时,有10%的几率掉下来的是两颗糖——这有
点gambl i n g的意思,但这只是⼀个游戏。
这个时候我们发现⼀切都变得如此郁闷,我们不但要加入⼀个“赢家”的状态, 还有在每⼀个动作
中都要加入⼀些判断是不是在这个状态的判定和后续动作定义。这并不是我们想要的。
2.对于此,我们的设计应该首先定义⼀个State接口,在这个接口中,糖果机的每⼀个动作都有
⼀个对应的方法。在设计这个接口时,我们的原则是:我们倾向于使用抽象类,这样想加入新
的方法就容易的多,但是没有共同的功能(的实现)可以放入其中的则用接口。
pu bl i c i n terface State {
pu bl i c v oi d i n sertQu arter();
pu bl i c v oi d ej ectQu arter();
pu bl i c v oi d tu rn Cran k();
pu bl i c v oi d di spen se();
}
然后我们对每个状态实现状态类,我们将动作委托到状态类中。
例如:没有投币的状态NoQu arterState
pu bl i c cl ass NoQu arterState i mpl emen t s State {
Gu mbal l Mach i n e gu mbal l Mach i n e;
pu bl i c NoQu arterState(Gu mbal l Mach i n e gu mbal l Mach i n e) {
th i s.gu mbal l Mach i n e = gu mbal l Mach i n e;
}
pu bl i c v oi d i n sertQu arter() {
Sy stem.ou t .pri n t l n ("Y ou i n serted a qu arter");
gumbal lMachine.setState(gumbal lMachine.getHasQuarterState()); / /状态转移
}
pu bl i c v oi d ej ectQu arter() {
Sy stem.ou t .pri n t l n ("Y ou h av en 't i n serted a qu arter");
}
pu bl i c v oi d tu rn Cran k() {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed, bu t th ere's n o qu arter");
}
pu bl i c v oi d di spen se() {
Sy stem.ou t .pri n t l n ("Y ou n eed to pay f i rst ");
}
pu bl i c St ri n g toSt ri n g() {
retu rn "wai t i n g for qu arter";
}
}
3.我们将这些状态整合到⼀个类中——糖果机类,在这个类的设计中分为两大部分——对于状态
的set ter和get ter,对于状态方法的封装(委托给当前的类)。另外这样我们就得到⼀个糖果机
了:
pu bl i c cl ass Gu mbal l Mach i n e {
State sol dOu tState;
State n oQu arterState;
State h asQu arterState;
State sol dState;
State state = soldOutState; / /当前状态
i n t cou n t = 0;
pu bl i c Gu mbal l Mach i n e(i n t n u mberGu mbal l s) {/ /开始的时候就用这个糖果机的类创建好各个状
态以便后边的时候
sol dOu tState = n ew Sol dOu tState(th i s);
n oQu arterState = n ew NoQu arterState(th i s);
h asQu arterState = n ew HasQu arterState(th i s);
sol dState = n ew Sol dState(th i s);
th i s. cou n t = n u mberGu mbal l s;
i f (n u mberGu mbal l s > 0) {
state = n oQu arterState;
}
}
pu bl i c v oi d i n sertQu arter() {
state. i n sertQu arter();
}
pu bl i c v oi d ej ectQu arter() {
state.ej ectQu arter();
}
pu bl i c v oi d tu rn Cran k() {
state. tu rn Cran k();
state.di spen se();
}
v oi d setState(State state) {
th i s. state = state;
}
v oi d rel easeBal l () {
Sy stem.ou t .pri n t l n ("A gu mbal l comes rol l i n g ou t th e sl ot . . . ");
i f (cou n t ! = 0) {
cou n t = cou n t - 1;
}
}
i n t getCou n t () {
retu rn cou n t ;
}
v oi d ref i l l (i n t cou n t ) {
th i s. cou n t = cou n t ;
state = n oQu arterState;
}
pu bl i c State getState() {
retu rn state;
}
pu bl i c State getSol dOu tState() {
retu rn sol dOu tState;
}
pu bl i c State getNoQu arterState() {
retu rn n oQu arterState;
}
pu bl i c State getHasQu arterState() {
retu rn h asQu arterState;
}
pu bl i c State getSol dState() {
retu rn sol dState;
}
pu bl i c St ri n g toSt ri n g() {
}
}
我们现在写⼀个测试类,用这个糖果机买点糖果出来,并测试⼀下这个机器的逻辑是不是正确
:
pu bl i c cl ass Gu mbal l Mach i n eT estDri v e {
pu bl i c stat i c v oi d mai n (St ri n g[ ] args) {
Gu mbal l Mach i n e gu mbal l Mach i n e = n ew Gu mbal l Mach i n e(5);
Sy stem.ou t .pri n t l n (gu mbal l Mach i n e);
gu mbal l Mach i n e. i n sertQu arter();
gu mbal l Mach i n e. tu rn Cran k();
Sy stem.ou t .pri n t l n (gu mbal l Mach i n e);
gu mbal l Mach i n e. i n sertQu arter();
gu mbal l Mach i n e. tu rn Cran k();
gu mbal l Mach i n e. i n sertQu arter();
gu mbal l Mach i n e. tu rn Cran k();
Sy stem.ou t .pri n t l n (gu mbal l Mach i n e);
}
}
4.这时,我们再考虑那个10%中奖率的小游戏就简单许多了。我们在Gu mbal l Mach i n e 中加入⼀
个新的状态wi n n erState,虽然我们可以把这个逻辑放入原来发放糖果的状态,但是就违背了“⼀
个类,⼀个职责”的原则:
pu bl i c cl ass Wi n n erState i mpl emen t s State {
Gu mbal l Mach i n e gu mbal l Mach i n e;
pu bl i c Wi n n erState(Gu mbal l Mach i n e gu mbal l Mach i n e) {
th i s.gu mbal l Mach i n e = gu mbal l Mach i n e;
}
pu bl i c v oi d i n sertQu arter() {
Sy stem.ou t .pri n t l n ("Pl ease wai t , we're al ready gi v i n g y ou a Gu mbal l ");
}
pu bl i c v oi d ej ectQu arter() {
Sy stem.ou t .pri n t l n ("Pl ease wai t , we're al ready gi v i n g y ou a Gu mbal l ");
}
pu bl i c v oi d tu rn Cran k() {
Sy stem.ou t .pri n t l n ("T u rn i n g agai n doesn 't get y ou an oth er gu mbal l ! ");
}
pu bl i c v oi d di spen se() {
Sy stem.ou t .pri n t l n ("Y OU'RE A WI NNER! Y ou get two gu mbal l s for y ou r qu arter");
gu mbal l Mach i n e. rel easeBal l ();
i f (gu mbal l Mach i n e.getCou n t () == 0) {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getSol dOu tState());
} el se {
gu mbal l Mach i n e. rel easeBal l ();
i f (gu mbal l Mach i n e.getCou n t () > 0) {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getNoQu arterState());
} el se {
Sy stem.ou t .pri n t l n ("Oops, ou t of gu mbal l s! ");
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getSol dOu tState());
}
}
}
pu bl i c St ri n g toSt ri n g() {
retu rn "despen si n g two gu mbal l s for y ou r qu arter , becau se Y OU'RE A WI NNER! ";
}
}
我们再加入10%成为赢家的逻辑:
pu bl i c cl ass HasQu arterState i mpl emen t s State {
Ran dom ran domWi n n er = n ew Ran dom(Sy stem. cu rren tT i meMi l l i s());
Gu mbal l Mach i n e gu mbal l Mach i n e;
pu bl i c HasQu arterState(Gu mbal l Mach i n e gu mbal l Mach i n e) {
th i s.gu mbal l Mach i n e = gu mbal l Mach i n e;
}
pu bl i c v oi d i n sertQu arter() {
Sy stem.ou t .pri n t l n ("Y ou can 't i n sert an oth er qu arter");
}
pu bl i c v oi d ej ectQu arter() {
Sy stem.ou t .pri n t l n ("Qu arter retu rn ed");
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getNoQu arterState());
}
pu bl i c v oi d tu rn Cran k() {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed. . . ");
int winner = randomWinner .nextInt(10);
i f ((winner == 0) && (gumbal lMachine.getCount() > 1)) {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getWi n n erState());
} el se {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getSol dState());
}
}
pu bl i c v oi d di spen se() {
Sy stem.ou t .pri n t l n ("No gu mbal l di spen sed");
}
pu bl i c St ri n g toSt ri n g() {
retu rn "wai t i n g for tu rn of cran k";
}
}
5.我们给这样的状态机设计⼀个名字就是“状态模式”——允许对象在内部状态改变时改变它的行
为,对象在外边看起来就像是修改了它的类⼀样。也就是说,里面的逻辑对外是不可见的,但
同时实际的状态,或者说参与的逻辑又是高度可扩展的。记得,不要让外界直接接触状态,这
样就破坏了面向对象设计的原则。
我们可以拿状态模式与策略模式做个对比,前者在内部定了各个逻辑步骤和状态转移,随着程
序的进行对外呈现不同的样子,这是对多个条件判断的⼀种替代方案,而后者则是在⼀开始由
外部主动指定要组合的策略对象是哪些,这是对继承之外的⼀种替代方案。
6.我们可以把这个设计再提升⼀下:
1) 我们可以把State设计为抽象类,将⼀些方法默认的行为,尤其是出错的行为放入其中,毕竟
有些出错提示是颇具有通用性的,可以供子状态去继承。
2)当我们没有投币时也可以调用转动曲柄的方法,这实际上是不合理的,我们可以通过加入⼀个
布尔类型来屏蔽这⼀bu g。
1.我们这次的话题是要实现⼀个糖果机,这个糖果机中如下的⼀些状态以指导糖果机的转移:
我们根据这个状态机写了⼀个有i f . .el se为主体的状态机程序,对每⼀个状态的转入转出做了实现
:
pu bl i c cl ass Gu mbal l Mach i n e {
f i n al stat i c i n t SOLD_OUT = 0;
f i n al stat i c i n t NO_QUARTER = 1;
f i n al stat i c i n t HAS_QUARTER = 2;
f i n al stat i c i n t SOLD = 3;
i n t state = SOLD_OUT ;
i n t cou n t = 0;
pu bl i c Gu mbal l Mach i n e(i n t cou n t ) {
th i s. cou n t = cou n t ;
i f (cou n t > 0) {
state = NO_QUARTER;
}
}
pu bl i c v oi d i n sertQu arter() {
i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou can 't i n sert an oth er qu arter");
} el se i f (state == NO_QUARTER) {
state = HAS_QUARTER;
Sy stem.ou t .pri n t l n ("Y ou i n serted a qu arter");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("Y ou can 't i n sert a qu arter , th e mach i n e i s sol d ou t ");
} el se i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("Pl ease wai t , we're al ready gi v i n g y ou a gu mbal l ");
}
}
pu bl i c v oi d ej ectQu arter() {
i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("Qu arter retu rn ed");
state = NO_QUARTER;
} el se i f (state == NO_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou h av en 't i n serted a qu arter");
} el se i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("Sorry , y ou al ready tu rn ed th e cran k");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("Y ou can 't ej ect , y ou h av en 't i n serted a qu arter y et ");
}
}
pu bl i c v oi d tu rn Cran k() {
i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("T u rn i n g twi ce doesn 't get y ou an oth er gu mbal l ! ");
} el se i f (state == NO_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed bu t th ere's n o qu arter");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed, bu t th ere are n o gu mbal l s");
} el se i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed. . . ");
state = SOLD;
di spen se();
}
}
pu bl i c v oi d di spen se() {
i f (state == SOLD) {
Sy stem.ou t .pri n t l n ("A gu mbal l comes rol l i n g ou t th e sl ot ");
cou n t = cou n t - 1;
i f (cou n t == 0) {
Sy stem.ou t .pri n t l n ("Oops, ou t of gu mbal l s! ");
state = SOLD_OUT ;
} el se {
state = NO_QUARTER;
}
} el se i f (state == NO_QUARTER) {
Sy stem.ou t .pri n t l n ("Y ou n eed to pay f i rst ");
} el se i f (state == SOLD_OUT) {
Sy stem.ou t .pri n t l n ("No gu mbal l di spen sed");
} el se i f (state == HAS_QUARTER) {
Sy stem.ou t .pri n t l n ("No gu mbal l di spen sed");
}
}
pu bl i c v oi d ref i l l (i n t n u mGu mBal l s) {
th i s. cou n t = n u mGu mBal l s;
state = NO_QUARTER;
}
pu bl i c St ri n g toSt ri n g() {
}
}
但是现在这个公司有了新的要求:当曲柄被转动时,有10%的几率掉下来的是两颗糖——这有
点gambl i n g的意思,但这只是⼀个游戏。
这个时候我们发现⼀切都变得如此郁闷,我们不但要加入⼀个“赢家”的状态, 还有在每⼀个动作
中都要加入⼀些判断是不是在这个状态的判定和后续动作定义。这并不是我们想要的。
2.对于此,我们的设计应该首先定义⼀个State接口,在这个接口中,糖果机的每⼀个动作都有
⼀个对应的方法。在设计这个接口时,我们的原则是:我们倾向于使用抽象类,这样想加入新
的方法就容易的多,但是没有共同的功能(的实现)可以放入其中的则用接口。
pu bl i c i n terface State {
pu bl i c v oi d i n sertQu arter();
pu bl i c v oi d ej ectQu arter();
pu bl i c v oi d tu rn Cran k();
pu bl i c v oi d di spen se();
}
然后我们对每个状态实现状态类,我们将动作委托到状态类中。
例如:没有投币的状态NoQu arterState
pu bl i c cl ass NoQu arterState i mpl emen t s State {
Gu mbal l Mach i n e gu mbal l Mach i n e;
pu bl i c NoQu arterState(Gu mbal l Mach i n e gu mbal l Mach i n e) {
th i s.gu mbal l Mach i n e = gu mbal l Mach i n e;
}
pu bl i c v oi d i n sertQu arter() {
Sy stem.ou t .pri n t l n ("Y ou i n serted a qu arter");
gumbal lMachine.setState(gumbal lMachine.getHasQuarterState()); / /状态转移
}
pu bl i c v oi d ej ectQu arter() {
Sy stem.ou t .pri n t l n ("Y ou h av en 't i n serted a qu arter");
}
pu bl i c v oi d tu rn Cran k() {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed, bu t th ere's n o qu arter");
}
pu bl i c v oi d di spen se() {
Sy stem.ou t .pri n t l n ("Y ou n eed to pay f i rst ");
}
pu bl i c St ri n g toSt ri n g() {
retu rn "wai t i n g for qu arter";
}
}
3.我们将这些状态整合到⼀个类中——糖果机类,在这个类的设计中分为两大部分——对于状态
的set ter和get ter,对于状态方法的封装(委托给当前的类)。另外这样我们就得到⼀个糖果机
了:
pu bl i c cl ass Gu mbal l Mach i n e {
State sol dOu tState;
State n oQu arterState;
State h asQu arterState;
State sol dState;
State state = soldOutState; / /当前状态
i n t cou n t = 0;
pu bl i c Gu mbal l Mach i n e(i n t n u mberGu mbal l s) {/ /开始的时候就用这个糖果机的类创建好各个状
态以便后边的时候
sol dOu tState = n ew Sol dOu tState(th i s);
n oQu arterState = n ew NoQu arterState(th i s);
h asQu arterState = n ew HasQu arterState(th i s);
sol dState = n ew Sol dState(th i s);
th i s. cou n t = n u mberGu mbal l s;
i f (n u mberGu mbal l s > 0) {
state = n oQu arterState;
}
}
pu bl i c v oi d i n sertQu arter() {
state. i n sertQu arter();
}
pu bl i c v oi d ej ectQu arter() {
state.ej ectQu arter();
}
pu bl i c v oi d tu rn Cran k() {
state. tu rn Cran k();
state.di spen se();
}
v oi d setState(State state) {
th i s. state = state;
}
v oi d rel easeBal l () {
Sy stem.ou t .pri n t l n ("A gu mbal l comes rol l i n g ou t th e sl ot . . . ");
i f (cou n t ! = 0) {
cou n t = cou n t - 1;
}
}
i n t getCou n t () {
retu rn cou n t ;
}
v oi d ref i l l (i n t cou n t ) {
th i s. cou n t = cou n t ;
state = n oQu arterState;
}
pu bl i c State getState() {
retu rn state;
}
pu bl i c State getSol dOu tState() {
retu rn sol dOu tState;
}
pu bl i c State getNoQu arterState() {
retu rn n oQu arterState;
}
pu bl i c State getHasQu arterState() {
retu rn h asQu arterState;
}
pu bl i c State getSol dState() {
retu rn sol dState;
}
pu bl i c St ri n g toSt ri n g() {
}
}
我们现在写⼀个测试类,用这个糖果机买点糖果出来,并测试⼀下这个机器的逻辑是不是正确
:
pu bl i c cl ass Gu mbal l Mach i n eT estDri v e {
pu bl i c stat i c v oi d mai n (St ri n g[ ] args) {
Gu mbal l Mach i n e gu mbal l Mach i n e = n ew Gu mbal l Mach i n e(5);
Sy stem.ou t .pri n t l n (gu mbal l Mach i n e);
gu mbal l Mach i n e. i n sertQu arter();
gu mbal l Mach i n e. tu rn Cran k();
Sy stem.ou t .pri n t l n (gu mbal l Mach i n e);
gu mbal l Mach i n e. i n sertQu arter();
gu mbal l Mach i n e. tu rn Cran k();
gu mbal l Mach i n e. i n sertQu arter();
gu mbal l Mach i n e. tu rn Cran k();
Sy stem.ou t .pri n t l n (gu mbal l Mach i n e);
}
}
4.这时,我们再考虑那个10%中奖率的小游戏就简单许多了。我们在Gu mbal l Mach i n e 中加入⼀
个新的状态wi n n erState,虽然我们可以把这个逻辑放入原来发放糖果的状态,但是就违背了“⼀
个类,⼀个职责”的原则:
pu bl i c cl ass Wi n n erState i mpl emen t s State {
Gu mbal l Mach i n e gu mbal l Mach i n e;
pu bl i c Wi n n erState(Gu mbal l Mach i n e gu mbal l Mach i n e) {
th i s.gu mbal l Mach i n e = gu mbal l Mach i n e;
}
pu bl i c v oi d i n sertQu arter() {
Sy stem.ou t .pri n t l n ("Pl ease wai t , we're al ready gi v i n g y ou a Gu mbal l ");
}
pu bl i c v oi d ej ectQu arter() {
Sy stem.ou t .pri n t l n ("Pl ease wai t , we're al ready gi v i n g y ou a Gu mbal l ");
}
pu bl i c v oi d tu rn Cran k() {
Sy stem.ou t .pri n t l n ("T u rn i n g agai n doesn 't get y ou an oth er gu mbal l ! ");
}
pu bl i c v oi d di spen se() {
Sy stem.ou t .pri n t l n ("Y OU'RE A WI NNER! Y ou get two gu mbal l s for y ou r qu arter");
gu mbal l Mach i n e. rel easeBal l ();
i f (gu mbal l Mach i n e.getCou n t () == 0) {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getSol dOu tState());
} el se {
gu mbal l Mach i n e. rel easeBal l ();
i f (gu mbal l Mach i n e.getCou n t () > 0) {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getNoQu arterState());
} el se {
Sy stem.ou t .pri n t l n ("Oops, ou t of gu mbal l s! ");
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getSol dOu tState());
}
}
}
pu bl i c St ri n g toSt ri n g() {
retu rn "despen si n g two gu mbal l s for y ou r qu arter , becau se Y OU'RE A WI NNER! ";
}
}
我们再加入10%成为赢家的逻辑:
pu bl i c cl ass HasQu arterState i mpl emen t s State {
Ran dom ran domWi n n er = n ew Ran dom(Sy stem. cu rren tT i meMi l l i s());
Gu mbal l Mach i n e gu mbal l Mach i n e;
pu bl i c HasQu arterState(Gu mbal l Mach i n e gu mbal l Mach i n e) {
th i s.gu mbal l Mach i n e = gu mbal l Mach i n e;
}
pu bl i c v oi d i n sertQu arter() {
Sy stem.ou t .pri n t l n ("Y ou can 't i n sert an oth er qu arter");
}
pu bl i c v oi d ej ectQu arter() {
Sy stem.ou t .pri n t l n ("Qu arter retu rn ed");
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getNoQu arterState());
}
pu bl i c v oi d tu rn Cran k() {
Sy stem.ou t .pri n t l n ("Y ou tu rn ed. . . ");
int winner = randomWinner .nextInt(10);
i f ((winner == 0) && (gumbal lMachine.getCount() > 1)) {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getWi n n erState());
} el se {
gu mbal l Mach i n e. setState(gu mbal l Mach i n e.getSol dState());
}
}
pu bl i c v oi d di spen se() {
Sy stem.ou t .pri n t l n ("No gu mbal l di spen sed");
}
pu bl i c St ri n g toSt ri n g() {
retu rn "wai t i n g for tu rn of cran k";
}
}
5.我们给这样的状态机设计⼀个名字就是“状态模式”——允许对象在内部状态改变时改变它的行
为,对象在外边看起来就像是修改了它的类⼀样。也就是说,里面的逻辑对外是不可见的,但
同时实际的状态,或者说参与的逻辑又是高度可扩展的。记得,不要让外界直接接触状态,这
样就破坏了面向对象设计的原则。
我们可以拿状态模式与策略模式做个对比,前者在内部定了各个逻辑步骤和状态转移,随着程
序的进行对外呈现不同的样子,这是对多个条件判断的⼀种替代方案,而后者则是在⼀开始由
外部主动指定要组合的策略对象是哪些,这是对继承之外的⼀种替代方案。
6.我们可以把这个设计再提升⼀下:
1) 我们可以把State设计为抽象类,将⼀些方法默认的行为,尤其是出错的行为放入其中,毕竟
有些出错提示是颇具有通用性的,可以供子状态去继承。
2)当我们没有投币时也可以调用转动曲柄的方法,这实际上是不合理的,我们可以通过加入⼀个
布尔类型来屏蔽这⼀bu g。