標(biāo)簽: python 設(shè)計(jì)模式
引子
狀態(tài)模式確實(shí)很好玩卖氨,我是說(shuō)書上的例子確實(shí)很好玩坞淮,我對(duì)著電腦玩了好長(zhǎng)時(shí)間当纱,但是想說(shuō)清楚還真不太容易嘀略,先從容易的開始吧
糖果機(jī)
糖果機(jī)操作的流程如下所示恤溶,這張圖也叫狀態(tài)圖,它顯示了糖果機(jī)的工作流程及狀態(tài)流程
狀態(tài)圖1
其中一共有四個(gè)狀態(tài)
- 售出糖果
- 糖果售罄
- 有25分錢
- 沒(méi)有25分錢
操作糖果機(jī)會(huì)設(shè)計(jì)四個(gè)動(dòng)作 - 投入25分錢
- 退回25分錢
- 轉(zhuǎn)動(dòng)曲柄
- 發(fā)放糖果 這個(gè)動(dòng)作是糖果機(jī)內(nèi)部的動(dòng)作帜羊,機(jī)器自己調(diào)用
一開始...
創(chuàng)建一個(gè)糖果機(jī)的類咒程,包含了四種狀態(tài)
class GumballMachine:
def __init__(self, count):
self.SOLD_OUT = 0 #糖果售罄狀態(tài)
self.NO_QUARTER = 1 #沒(méi)有25分錢狀態(tài)
self.HAS_QUARTER = 2 #有25分錢狀態(tài)
self.SOLD = 3 #售出糖果狀態(tài)
self.state = self.SOLD_OUT #初始狀態(tài)為`沒(méi)有25分錢狀態(tài)`
self.count = count #設(shè)置一個(gè)糖果數(shù)量變量,它為0時(shí)就是糖果售罄的狀態(tài)
if self.count > 0: #此處如果糖果數(shù)大于0讼育,則狀態(tài)為初始狀態(tài)
self.state = self.NO_QUARTER
接下來(lái)該怎么辦呢帐姻?
按照上圖,狀態(tài)與狀態(tài)之間是通過(guò)動(dòng)作進(jìn)行連接的奶段,可以對(duì)每一個(gè)動(dòng)作創(chuàng)建一個(gè)對(duì)應(yīng)的方法饥瓷,這些方法利用條件語(yǔ)句來(lái)決定在四個(gè)狀態(tài)下的恰當(dāng)行為
例如投入25分錢和這個(gè)動(dòng)作在四種狀態(tài)下糖果機(jī)的反應(yīng)如下圖
狀態(tài)圖2
動(dòng)作代表當(dāng)前執(zhí)行的動(dòng)作是
投入25分錢
如果糖果機(jī)當(dāng)前狀態(tài)是
沒(méi)有25分錢
,則糖果機(jī)行為是顯示你投入了25分錢
痹籍,之后狀態(tài)要切換到有25分錢
如果糖果機(jī)當(dāng)前狀態(tài)是
有25分錢
呢铆,則糖果機(jī)行為是顯示投多了
如果糖果機(jī)當(dāng)前狀態(tài)是
糖果售罄
,則糖果機(jī)行為是顯示售光了蹲缠,不能投了
如果糖果機(jī)當(dāng)前狀態(tài)是
售出糖果
棺克,則糖果機(jī)行為是顯示投太快了,稍等
线定,因?yàn)槭鄢鎏枪竽纫辏枪麢C(jī)要恢復(fù)到初始狀態(tài)沒(méi)有25分錢
剩下的動(dòng)作和這個(gè)沒(méi)有什么區(qū)別,每個(gè)動(dòng)作都有一個(gè)狀態(tài)轉(zhuǎn)換的步驟
代碼
def insertQuarter(self): #投入25分錢動(dòng)作
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分錢動(dòng)作
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): #轉(zhuǎn)動(dòng)曲柄動(dòng)作
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() #切換到發(fā)放糖果這個(gè)內(nèi)部動(dòng)作上
def dispense(self): #發(fā)放糖果動(dòng)作
if self.state == self.SOLD:
print("A gumball comes rolling out the slot")
self.count = self.count - 1 #發(fā)放一次糖果斤讥,糖果數(shù)量要減1
if self.count == 0: #糖果數(shù)量為0了纱皆,切換到糖果售罄的狀態(tài)
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")
玩一玩....
可以增加兩個(gè)方法,玩的時(shí)候?qū)崟r(shí)查看當(dāng)前狀態(tài)和糖果數(shù)量
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()
返回的結(jié)果
2 #兩個(gè)糖果
1 #沒(méi)有25分錢狀態(tài)
=====================================================
You insert a quarter
2 #有25分錢狀態(tài)
Quarter returned
You haven't inserted a quarter
You insert a quarter
2 #有25分錢狀態(tài)
You turned....
A gumball comes rolling out the slot
1 #沒(méi)有25分錢狀態(tài)
1 #一個(gè)糖果
You insert a quarter
You turned....
A gumball comes rolling out the slot
Oops, out of gumballs
0 #糖果售罄狀態(tài)
=====================================================
You turned, but there's no gumball
這個(gè)時(shí)候如果又來(lái)了一個(gè)狀態(tài)怎么辦...
按照狀態(tài)2圖周偎,需要增加一個(gè)新的狀態(tài)抹剩,之后在每個(gè)動(dòng)作里面增加針對(duì)這個(gè)狀態(tài)的行為,好像違反了好多設(shè)計(jì)原則蓉坎。
新的方法
狀態(tài)是變化的量,將變化封裝起來(lái)胡嘿,將動(dòng)作
和行為
放到狀態(tài)
里蛉艾,這樣每個(gè)狀態(tài)只要實(shí)現(xiàn)它自己的那套行為。
為每個(gè)狀態(tài)創(chuàng)建狀態(tài)類,這些類負(fù)責(zé)在對(duì)應(yīng)的動(dòng)作下糖果機(jī)的行為
將動(dòng)作和行為委托給狀態(tài)類
狀態(tài)圖3
之前的狀態(tài)圖2現(xiàn)在要變成這樣了勿侯,用狀態(tài)包裹所有的動(dòng)作及對(duì)應(yīng)的行為
有25分錢
狀態(tài)下拓瞪,每一個(gè)動(dòng)作對(duì)應(yīng)不同的行為,在執(zhí)行退回25錢
動(dòng)作后助琐,狀態(tài)切換到沒(méi)有25分錢
狀態(tài)祭埂,在執(zhí)行轉(zhuǎn)動(dòng)曲柄
動(dòng)作后,狀態(tài)切換到沒(méi)有25分錢
狀態(tài)兵钮。代碼
先看狀態(tài)圖3的代碼實(shí)現(xiàn)
#有25分錢狀態(tài)
class HasQuarterState(object):
def __init__(self, gumballMachine): #傳入糖果機(jī)的實(shí)例
self.gumballMachine = gumballMachine
def insertQuarter(self): #投入25分錢動(dòng)作
print("You cannot insert another quarter")
def ejectQuarter(self): #退出25分錢動(dòng)作
print("Quarter returned")
self.gumballMachine.setState(self.gumballMachine.getNoQuarterState()) #之后糖果機(jī)的狀
#態(tài)切換到?jīng)]有25分錢狀態(tài)
def turnCrank(self): #轉(zhuǎn)動(dòng)曲柄動(dòng)作
print("You turned....")
self.gumballMachine.setState(self.gumballMachine.getSoldState()) #之后糖果機(jī)的狀
#態(tài)切換到售出糖果狀態(tài)
def dispense(self): #發(fā)放糖果動(dòng)作蛆橡,這是個(gè)內(nèi)部動(dòng)作,此處實(shí)現(xiàn)沒(méi)有作用
print("No gumball dispense")
其他狀態(tài)的代碼也是類似
#糖果售罄狀態(tài)
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")
#沒(méi)有25分錢狀態(tài)
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")
#售出糖果狀態(tài)
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())
看看糖果機(jī)的實(shí)現(xiàn)
#糖果機(jī)類
class GumballMachine:
def __init__(self, numberGumballs):
self.count = numberGumballs
#=========創(chuàng)建每一個(gè)狀態(tài)的狀態(tài)實(shí)例====================#
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
#============每個(gè)狀態(tài)的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=========================================#
#============將方法委托給當(dāng)前的狀態(tài)===================#
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
#============檢查狀態(tài)和糖果數(shù)量的方法=================#
def getState(self):
print(self.state)
def getCount(self):
return self.count
還是用之前得測(cè)試代碼掘譬,看看返回
2 #糖果數(shù)量
<__main__.NoQuarterState object at 0x01D6BC90> #當(dāng)前狀態(tài)
=====================================================
You inserted a quarter #行為
<__main__.HasQuarterState object at 0x01D6BCB0> #當(dāng)前狀態(tài)
Quarter returned #行為
You haven't inserted a quarter #行為
You inserted a quarter #行為
<__main__.HasQuarterState object at 0x01D6BCB0> #當(dāng)前狀態(tài)
You turned.... #行為
A gumball comes rolling out the slot... #行為
<__main__.NoQuarterState object at 0x01D6BC90> #當(dāng)前狀態(tài)
1 #糖果數(shù)量
You inserted a quarter #行為
You turned.... #行為
A gumball comes rolling out the slot... #行為
Oops, out of gumballs #行為
<__main__.SoldOutState object at 0x01D6BC70> #當(dāng)前狀態(tài)
=====================================================
You turned, but there's no gumball #行為
看看執(zhí)行圖
初始狀態(tài)是
沒(méi)有25分錢
泰演,執(zhí)行投入25分錢
動(dòng)作糖果機(jī)切換狀態(tài)到第二步狀態(tài)
有25分錢
,之后執(zhí)行轉(zhuǎn)動(dòng)曲柄
動(dòng)作糖果機(jī)切換狀態(tài)到第三步狀態(tài)
售出糖果
葱轩,之后執(zhí)行發(fā)放糖果
動(dòng)作如果糖果數(shù)目為0睦焕,則糖果機(jī)切換狀態(tài)到第四步狀態(tài)
糖果售罄
糖果機(jī)動(dòng)作和行為都委托給了每種狀態(tài),狀態(tài)一變靴拱,糖果機(jī)的行為就是此種狀態(tài)下的動(dòng)作產(chǎn)生的行為了垃喊,這樣一來(lái),如果增加了一種狀態(tài)袜炕,只要單獨(dú)實(shí)現(xiàn)這個(gè)狀態(tài)下糖果機(jī)所有的行為就OK了缔御。
再來(lái)一個(gè)狀態(tài)
增加一個(gè)游戲狀態(tài),轉(zhuǎn)曲柄獲取糖果的時(shí)候妇蛀,有10%的機(jī)會(huì)能成為大贏家耕突,獲得附贈(zèng)的一粒糖果,這個(gè)怎么搞评架?
增加一個(gè)狀態(tài)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!")
# 你贏了眷茁,如果糖果沒(méi)了,那就算了纵诞,只能白贏了上祈;
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())
隨機(jī)數(shù)怎么整,random.randint這個(gè)就能實(shí)現(xiàn)浙芙,但是這個(gè)動(dòng)作要增加在哪里呢登刺,哪個(gè)狀態(tài)下轉(zhuǎn)動(dòng)曲柄可以獲得糖果,是有25分錢
這個(gè)狀態(tài)嗡呼,只要將這個(gè)狀態(tài)下的轉(zhuǎn)曲柄動(dòng)作稍微改動(dòng)一下就OK了
def turnCrank(self):
print("You turned....")
#產(chǎn)生隨機(jī)數(shù)
self.winner = random.randint(1, 100)
#這個(gè)數(shù)為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次幾率好像太小纸俭,應(yīng)該中不了
======================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
靠!D洗啊W岷堋@陕ァ!第一次就中了
定義
狀態(tài)模式允許對(duì)象在內(nèi)部狀態(tài)改變時(shí)改變它的行為窒悔,對(duì)象看起來(lái)好像修改了它的類