标签: 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
靠!!!!!第一次就中了
定义
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类