问题:
有一个糖果公司须要设计一个糖果售卖机,控制流程以下图,须要怎么实现?python
这是一个状态图,每一个圆圈都是一种状态。很明显,有有25分钱
、 没有25分钱
、 售出糖果
、 糖果售罄
四个状态,同时也对应四个动做:投入25分钱
,退回25分钱
,转动曲柄
和发放糖果
。小程序
那如何从状态图获得真正的代码呢?设计模式
简单代码实现以下:并发
#! -*- coding: utf-8 -*-
class GumballMachine:
# 找出全部状态,并建立实例变量来持有当前状态,而后定义状态的值
STATE_SOLD_OUT = 0
STATE_NO_QUARTER = 1
STATE_HAS_QUARTER = 2
STATE_SOLD = 3
state = STATE_SOLD_OUT
def __init__(self, count=0):
self.count = count
if count > 0:
self.state = self.STATE_NO_QUARTER
def __str__(self):
return "Gumball machine current state: %s" % self.state
def insert_quarter(self):
# 投入25分钱
if self.state == self.STATE_HAS_QUARTER: # 若是已经投过
print("You can't insert another quarter")
elif self.state == self.STATE_NO_QUARTER: # 若是没有投过
self.state = self.STATE_HAS_QUARTER
print("You inserted a quarter")
elif self.state == self.STATE_SOLD_OUT: # 若是已经售罄
print("You can't insert a quarter, the machine is sold out")
elif self.state == self.STATE_SOLD: # 若是刚刚买了糖果
print("Please wait, we're already giving you a gumball")
def eject_quarter(self):
# 退回25分
if self.state == self.STATE_HAS_QUARTER:
print("Quarter returned")
self.state = self.STATE_NO_QUARTER
elif self.state == self.STATE_NO_QUARTER:
print("You haven't inserted a quarter")
elif self.state == self.STATE_SOLD:
print("Sorry, you alread turned the crank")
elif self.state == self.SOLD_OUT:
print("You can't eject, you haven't inserted")
def turn_crank(self):
# 转动曲柄
if self.state == self.STATE_SOLD:
print("Turning twice doesn't get you another gumball")
elif self.state == self.STATE_NO_QUARTER:
print("You turned but there's no quarter")
elif self.state == self.STATE_SOLD_OUT:
print("You turned, but there are no gumballs")
elif self.state == self.STATE_HAS_QUARTER:
print("You turned...")
self.state = self.STATE_SOLD
self.dispense()
def dispense(self):
# 发放糖果
if self.state == self.STATE_SOLD:
print("A gumball comes rolling out the slot")
self.count -= 1
if self.count == 0:
self.state = self.STATE_SOLD_OUT
else:
self.state = self.STATE_NO_QUARTER
elif self.state == self.STATE_NO_QUARTER:
print("You need to pay first")
elif self.state == self.STATE_SOLD_OUT:
print("No gumball dispensed")
elif self.state == self.STATE_HAS_QUARTER:
print("No gumball dispensed")
if __name__ == "__main__":
# 如下是代码测试
gumball_machine = GumballMachine(5) # 装入5 个糖果
print(gumball_machine)
gumball_machine.insert_quarter() # 投入25分钱
gumball_machine.turn_crank() # 转动曲柄
print(gumball_machine)
gumball_machine.insert_quarter() #投入25分钱
gumball_machine.eject_quarter() # 退钱
gumball_machine.turn_crank() # 转动曲柄
print(gumball_machine)
gumball_machine.insert_quarter() # 投入25分钱
gumball_machine.turn_crank() # 转动曲柄
gumball_machine.insert_quarter() # 投入25分钱
gumball_machine.turn_crank() # 转动曲柄
gumball_machine.eject_quarter() # 退钱
print(gumball_machine)
复制代码
这段代码有几个问题:dom
如何改进呢?测试
考虑封装变化,把每一个状态的行为都放在各自的类中,每一个状态只要实现本身的动做,用加入新类的方式来实现新状态的加入。spa
新的实现代码以下:设计
#! -*- coding: utf-8 -*-
class State:
# 定义state基类
def insert_quarter(self):
pass
def eject_quarter(self):
pass
def turn_crank(self):
pass
def dispense(self):
pass
class SoldOutState(State):
# 继承State 类
def __init__(self, gumball_machine):
self.gumball_machine = gumball_machine
def __str__(self):
return "sold_out"
def insert_quarter(self):
print("You can't insert a quarter, the machine is sold out")
def eject_quarter(self):
print("You can't eject, you haven't inserted a quarter yet")
def turn_crank(self):
print("You turned, but ther are no gumballs")
def dispense(self):
print("No gumball dispensed")
class SoldState(State):
# 继承State 类
def __init__(self, gumball_machine):
self.gumball_machine = gumball_machine
def __str__(self):
return "sold"
def insert_quarter(self):
print("Please wait, we're already giving you a gumball")
def eject_quarter(self):
print("Sorry, you already turned the crank")
def turn_crank(self):
print("Turning twice doesn't get you another gumball")
def dispense(self):
self.gumball_machine.release_ball()
if gumball_machine.count > 0:
self.gumball_machine.state = self.gumball_machine.no_quarter_state
else:
print("Oops, out of gumballs!")
self.gumball_machine.state = self.gumball_machine.soldout_state
class NoQuarterState(State):
# 继承State 类
def __init__(self, gumball_machine):
self.gumball_machine = gumball_machine
def __str__(self):
return "no_quarter"
def insert_quarter(self):
# 投币 而且改变状态
print("You inserted a quarter")
self.gumball_machine.state = self.gumball_machine.has_quarter_state
def eject_quarter(self):
print("You haven't insert a quarter")
def turn_crank(self):
print("You turned, but there's no quarter")
def dispense(self):
print("You need to pay first")
class HasQuarterState(State):
# 继承State 类
def __init__(self, gumball_machine):
self.gumball_machine = gumball_machine
def __str__(self):
return "has_quarter"
def insert_quarter(self):
print("You can't insert another quarter")
def eject_quarter(self):
print("Quarter returned")
self.gumball_machine.state = self.gumball_machine.no_quarter_state
def turn_crank(self):
print("You turned...")
self.gumball_machine.state = self.gumball_machine.sold_state
def dispense(self):
print("No gumball dispensed")
class GumballMachine:
def __init__(self, count=0):
self.count = count
# 找出全部状态,并建立实例变量来持有当前状态,而后定义状态的值
self.soldout_state = SoldOutState(self)
self.no_quarter_state = NoQuarterState(self)
self.has_quarter_state = HasQuarterState(self)
self.sold_state = SoldState(self)
if count > 0:
self.state = self.no_quarter_state
else:
self.state = self.soldout_state
def __str__(self):
return ">>> Gumball machine current state: %s" % self.state
def insert_quarter(self):
# 投入25分钱
self.state.insert_quarter()
def eject_quarter(self):
# 退回25分
self.state.eject_quarter()
# print("state", self.state, type(self.state))
def turn_crank(self):
# 转动曲柄
# print("state", self.state, type(self.state))
self.state.turn_crank() # 修改状态
self.state.dispense() # 发糖 感谢 @dyq666 指出错误
def release_ball(self):
# 发放糖果
print("A gumball comes rolling out the slot...")
if self.count > 0:
self.count -= 1
if __name__ == "__main__":
# 如下是代码测试
gumball_machine = GumballMachine(5) # 装入5 个糖果
print(gumball_machine)
gumball_machine.insert_quarter() # 投入25分钱
gumball_machine.turn_crank() # 转动曲柄
print(gumball_machine)
gumball_machine.insert_quarter() #投入25分钱
gumball_machine.eject_quarter() # 退钱
gumball_machine.turn_crank() # 转动曲柄
print(gumball_machine)
gumball_machine.insert_quarter() # 投入25分钱
gumball_machine.turn_crank() # 转动曲柄
gumball_machine.insert_quarter() # 投入25分钱
gumball_machine.turn_crank() # 转动曲柄
gumball_machine.eject_quarter() # 退钱
print(gumball_machine)
复制代码
重构后的代码相对于以前的代码作了哪些事情呢?code
状态类
对修改关闭,对糖果季类对扩展开放
下图是刚初始状态图示:cdn
上面重构部分代码使用的就是状态模式:
状态模式
: 状态模式容许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式的类图以下:
状态模式是将多个行为封装在状态对象中, context 的行为随时可委托到其中一个状态中。当前状态在不一样的状态对象中改变,以反映出context 内部的状态,context 的行为也会随之改变。
若是,如今要在这四个状态的基础上再加一个状态(购买糖果后,有10%的几率再得一个),该如何实现呢?
# 添加WinnerState 类,只有dispense 方法不一样,能够从SoldState 类继承
class WinnerState(SoldState):
def __str__(self):
return "winner"
def dispense(self):
print("You're a WINNER! You get two gumballs for your quarter")
self.gumball_machine.release_ball()
if gumball_machine.count == 0:
self.gumball_machine.state = self.gumball_machine.soldout_state
else:
self.gumball_machine.release_ball()
if gumball_machine.count > 0:
self.gumball_machine.state = self.gumball_machine.no_quarter_state
else:
print("Oops, out of gumballs!")
self.gumball_machine.state = self.gumball_machine.soldout_state
# 修改turn_crank 方法
class HasQuarterState(State):
...
def turn_crank(self):
print("You turned...")
winner = random.randint(0, 9)
if winner == 4 and self.gumball_machine.count > 1: # 若是库存大于 1 而且随机数等于4(能够是0到9任意值)
self.gumball_machine.state = self.gumball_machine.winner_state
else:
self.gumball_machine.state = self.gumball_machine.sold_state
# 在 GumballMachine 中初始化
class GumballMachine:
def __init__(self, count=0):
self.count = count
# 找出全部状态,并建立实例变量来持有当前状态,而后定义状态的值
...
self.winner_state = WinnerState(self)
...
复制代码
本文例子来自《Head First 设计模式》。
新年快乐。
最后,感谢女友支持和包容,比❤️
也能够在公号输入如下关键字获取历史文章:公号&小程序
| 设计模式
| 并发&协程