标签: python 设计模式
引子
状态模式确实很好玩,我是说书上的例子确实很好玩,我对着电脑玩了好长时间,但是想说清楚还真不太容易,先从容易的开始吧
糖果机
糖果机操作的流程如下所示,这张图也叫状态图,它显示了糖果机的工作流程及状态流程
状态图1
其中一共有四个状态
- 售出糖果
- 糖果售罄
- 有25分钱
- 没有25分钱
操作糖果机会设计四个动作 - 投入25分钱
- 退回25分钱
- 转动曲柄
- 发放糖果 这个动作是糖果机内部的动作,机器自己调用
一开始...
创建一个糖果机的类,包含了四种状态
class GumballMachine:
def __init__(self, count):
self.SOLD_OUT = 0 #糖果售罄状态
self.NO_QUARTER = 1 #没有25分钱状态
self.HAS_QUARTER = 2 #有25分钱状态
self.SOLD = 3 #售出糖果状态
self.state = self.SOLD_OUT #初始状态为`没有25分钱状态`
self.count = count #设置一个糖果数量变量,它为0时就是糖果售罄的状态
if self.count > 0: #此处如果糖果数大于0,则状态为初始状态
self.state = self.NO_QUARTER
接下来该怎么办呢?
按照上图,状态与状态之间是通过动作进行连接的,可以对每一个动作创建一个对应的方法,这些方法利用条件语句来决定在四个状态下的恰当行为
例如投入25分钱和这个动作在四种状态下糖果机的反应如下图
状态图2
动作代表当前执行的动作是
投入25分钱
如果糖果机当前状态是
没有25分钱
,则糖果机行为是显示你投入了25分钱
,之后状态要切换到有25分钱
如果糖果机当前状态是
有25分钱
,则糖果机行为是显示投多了
如果糖果机当前状态是
糖果售罄
,则糖果机行为是显示售光了,不能投了
如果糖果机当前状态是
售出糖果
,则糖果机行为是显示投太快了,稍等
,因为售出糖果后,糖果机要恢复到初始状态没有25分钱
剩下的动作和这个没有什么区别,每个动作都有一个状态转换的步骤
代码
def insertQuarter(self): #投入25分钱动作
if self.state == self.HAS_QUARTER:
print("You cannot insert another quarter")
elif self.state == self.NO_QUARTER:
self.state = self.HAS_QUARTER
print("You insert a quarter")
elif self.state == self.SOLD_OUT:
print("You can't insert a quarter, the machine is sold out")
elif self.state == self.SOLD:
print("Please wait, we're already giving you a gumball")
def ejectQuarter(self): #退回25分钱动作
if self.state == self.HAS_QUARTER:
print("Quarter returned")
self.state = self.NO_QUARTER
elif self.state == self.NO_QUARTER:
print("You haven't inserted a quarter")
elif self.state == self.SOLD:
print("Sorry, you already turned the crank")
elif self.state == self.SOLD_OUT:
print("You can't eject, you haven't inserted a quarter yet")
def turnCrank(self): #转动曲柄动作
if self.state == self.SOLD:
print("Turning twice doesn't get you another gumball!")
elif self.state == self.NO_QUARTER:
print("You turned, but there's no quarter")
elif self.state == self.SOLD_OUT:
print("You turned, but there's no gumball")
elif self.state == self.HAS_QUARTER:
print("You turned....")
self.state = self.SOLD
self.dispense() #切换到发放糖果这个内部动作上
def dispense(self): #发放糖果动作
if self.state == self.SOLD:
print("A gumball comes rolling out the slot")
self.count = self.count - 1 #发放一次糖果,糖果数量要减1
if self.count == 0: #糖果数量为0了,切换到糖果售罄的状态
print("Oops, out of gumballs")
self.state = self.SOLD_OUT
else:
self.state = self.NO_QUARTER
elif self.state == self.NO_QUARTER:
print("You need to pay first")
elif self.state == self.SOLD_OUT:
print("No gumball dispense")
elif self.state == self.HAS_QUARTER:
print("No gumball dispense")
玩一玩....
可以增加两个方法,玩的时候实时查看当前状态和糖果数量
def getCount(self):
print(self.count)
def getState(self):
print(self.state)
按照下面方法玩
def main():
gumballMachine = GumballMachine(2)
gumballMachine.getCount()
gumballMachine.getState()
print("=====================================================")
gumballMachine.insertQuarter()
gumballMachine.getState()
gumballMachine.ejectQuarter()
gumballMachine.ejectQuarter()
gumballMachine.insertQuarter()
gumballMachine.getState()
gumballMachine.turnCrank()
gumballMachine.getState()
gumballMachine.getCount()
gumballMachine.insertQuarter()
gumballMachine.turnCrank()
gumballMachine.getState()
print("=====================================================")
gumballMachine.turnCrank()
返回的结果
2 #两个糖果
1 #没有25分钱状态
=====================================================
You insert a quarter
2 #有25分钱状态
Quarter returned
You haven't inserted a quarter
You insert a quarter
2 #有25分钱状态
You turned....
A gumball comes rolling out the slot
1 #没有25分钱状态
1 #一个糖果
You insert a quarter
You turned....
A gumball comes rolling out the slot
Oops, out of gumballs
0 #糖果售罄状态
=====================================================
You turned, but there's no gumball
这个时候如果又来了一个状态怎么办...
按照状态2图,需要增加一个新的状态,之后在每个动作里面增加针对这个状态的行为,好像违反了好多设计原则。
新的方法
状态是变化的量,将变化封装起来,将动作
和行为
放到状态
里,这样每个状态只要实现它自己的那套行为。
为每个状态创建状态类,这些类负责在对应的动作下糖果机的行为
将动作和行为委托给状态类
状态图3
之前的状态图2现在要变成这样了,用状态包裹所有的动作及对应的行为
有25分钱
状态下,每一个动作对应不同的行为,在执行退回25钱
动作后,状态切换到没有25分钱
状态,在执行转动曲柄
动作后,状态切换到没有25分钱
状态。代码
先看状态图3的代码实现
#有25分钱状态
class HasQuarterState(object):
def __init__(self, gumballMachine): #传入糖果机的实例
self.gumballMachine = gumballMachine
def insertQuarter(self): #投入25分钱动作
print("You cannot insert another quarter")
def ejectQuarter(self): #退出25分钱动作
print("Quarter returned")
self.gumballMachine.setState(self.gumballMachine.getNoQuarterState()) #之后糖果机的状
#态切换到没有25分钱状态
def turnCrank(self): #转动曲柄动作
print("You turned....")
self.gumballMachine.setState(self.gumballMachine.getSoldState()) #之后糖果机的状
#态切换到售出糖果状态
def dispense(self): #发放糖果动作,这是个内部动作,此处实现没有作用
print("No gumball dispense")
其他状态的代码也是类似
#糖果售罄状态
class SoldOutState(object):
def __init__(self, gumballMachine):
self.gumballMachine = gumballMachine
def insertQuarter(self):
print("You can't insert a quarter, the machine is sold out")
def ejectQuarter(self):
print("You can't eject, you haven't inserted a quarter yet")
def turnCrank(self):
print("You turned, but there's no gumball")
def dispense(self):
print("No gumball dispense")
#没有25分钱状态
class NoQuarterState(object):
def __init__(self, gumballMachine):
self.gumballMachine = gumballMachine
def insertQuarter(self):
print("You inserted a quarter")
self.gumballMachine.setState(self.gumballMachine.getHasQuarterState())
def ejectQuarter(self):
print("You haven't inserted a quarter")
def turnCrank(self):
print("You turned, but there's no quarter")
def dispense(self):
print("You need to pay first")
#售出糖果状态
class SoldState(object):
def __init__(self, gumballMachine):
self.gumballMachine = gumballMachine
def insertQuarter(self):
print("Please wait, we're already giving you a gumball")
def ejectQuarter(self):
print("Sorry, you already turned the crank")
def turnCrank(self):
print("Turning twice doesn't get you another gumball!")
def dispense(self):
self.gumballMachine.releaseBall()
if self.gumballMachine.getCount()>0:
self.gumballMachine.setState(self.gumballMachine.getNoQuarterState())
else:
print("Oops, out of gumballs")
self.gumballMachine.setState(self.gumballMachine.getSoldOutState())
看看糖果机的实现
#糖果机类
class GumballMachine:
def __init__(self, numberGumballs):
self.count = numberGumballs
#=========创建每一个状态的状态实例====================#
self.soldOutState = SoldOutState(self)
self.noQuarterState = NoQuarterState(self)
self.hasQuarterState = HasQuarterState(self)
self.soldState = SoldState(self)
#=========end=========================================#
if self.count > 0:
self.state = self.noQuarterState
#============每个状态的get方法和set方法===============#
def getSoldOutState(self):
return self.soldOutState
def getNoQuarterState(self):
return self.noQuarterState
def getHasQuarterState(self):
return self.hasQuarterState
def getSoldState(self):
return self.soldState
def setState(self, state):
self.state = state
#=========end=========================================#
#============将方法委托给当前的状态===================#
def insertQuarter(self):
self.state.insertQuarter()
def ejectQuarter(self):
self.state.ejectQuarter()
def turnCrank(self):
if self.state == self.hasQuarterState:
self.state.turnCrank()
self.state.dispense()
else:
self.state.turnCrank()
#=========end=========================================#
def releaseBall(self):
print("A gumball comes rolling out the slot...")
if self.count != 0:
self.count -= 1
#============检查状态和糖果数量的方法=================#
def getState(self):
print(self.state)
def getCount(self):
return self.count
还是用之前得测试代码,看看返回
2 #糖果数量
<__main__.NoQuarterState object at 0x01D6BC90> #当前状态
=====================================================
You inserted a quarter #行为
<__main__.HasQuarterState object at 0x01D6BCB0> #当前状态
Quarter returned #行为
You haven't inserted a quarter #行为
You inserted a quarter #行为
<__main__.HasQuarterState object at 0x01D6BCB0> #当前状态
You turned.... #行为
A gumball comes rolling out the slot... #行为
<__main__.NoQuarterState object at 0x01D6BC90> #当前状态
1 #糖果数量
You inserted a quarter #行为
You turned.... #行为
A gumball comes rolling out the slot... #行为
Oops, out of gumballs #行为
<__main__.SoldOutState object at 0x01D6BC70> #当前状态
=====================================================
You turned, but there's no gumball #行为
看看执行图
初始状态是
没有25分钱
,执行投入25分钱
动作糖果机切换状态到第二步状态
有25分钱
,之后执行转动曲柄
动作糖果机切换状态到第三步状态
售出糖果
,之后执行发放糖果
动作如果糖果数目为0,则糖果机切换状态到第四步状态
糖果售罄
糖果机动作和行为都委托给了每种状态,状态一变,糖果机的行为就是此种状态下的动作产生的行为了,这样一来,如果增加了一种状态,只要单独实现这个状态下糖果机所有的行为就OK了。
再来一个状态
增加一个游戏状态,转曲柄获取糖果的时候,有10%的机会能成为大赢家,获得附赠的一粒糖果,这个怎么搞?
增加一个状态winnerState
class WinnerState(object):
def __init__(self, gumballMachine):
self.gumballMachine = gumballMachine
def insertQuarter(self):
print("Please wait, we're already giving you a gumball")
def ejectQuarter(self):
print("Sorry, you already turned the crank")
def turnCrank(self):
print("Turning twice doesn't get you another gumball!")
# 你赢了,如果糖果没了,那就算了,只能白赢了;
def dispense(self):
print("You are winner! You get 2 gumball for youe quarter")
if self.gumballMachine.getCount()==0:
self.gumballMachine.setState(self.gumballMachine.getSoldOutState())
else:
self.gumballMachine.releaseBall()
if self.gumballMachine.getCount()>0:
self.gumballMachine.releaseBall()
self.gumballMachine.setState(self.gumballMachine.getNoQuarterState())
else:
print("Oops, out of gumballs")
self.gumballMachine.setState(self.gumballMachine.getSoldOutState())
随机数怎么整,random.randint这个就能实现,但是这个动作要增加在哪里呢,哪个状态下转动曲柄可以获得糖果,是有25分钱
这个状态,只要将这个状态下的转曲柄动作稍微改动一下就OK了
def turnCrank(self):
print("You turned....")
#产生随机数
self.winner = random.randint(1, 100)
#这个数为1你就赢了
if self.winner == 1:
self.gumballMachine.setState(self.gumballMachine.getWinnerState())
else:
self.gumballMachine.setState(self.gumballMachine.getSoldState())
开始玩吧
def main():
gumballMachine = GumballMachine(100)
print(gumballMachine.getCount())
for i in range(5):
print("======================{0}====================".format(i+1))
gumballMachine.insertQuarter()
gumballMachine.turnCrank()
print(gumballMachine.getCount())
5次几率好像太小,应该中不了
======================1====================
You inserted a quarter
You turned....
You are winner! You get 2 gumball for youe quarter
A gumball comes rolling out the slot...
A gumball comes rolling out the slot...
======================2====================
You inserted a quarter
You turned....
A gumball comes rolling out the slot...
======================3====================
You inserted a quarter
You turned....
A gumball comes rolling out the slot...
======================4====================
You inserted a quarter
You turned....
A gumball comes rolling out the slot...
======================5====================
You inserted a quarter
You turned....
A gumball comes rolling out the slot...
94
靠!!!!!第一次就中了
定义
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类
网友评论