問題:
有一個糖果公司須要設計一個糖果售賣機,控制流程以下圖,須要怎麼實現?
這是一個狀態圖,每一個圓圈都是一種狀態。很明顯,有有25分錢
、 沒有25分錢
、 售出糖果
、 糖果售罄
四個狀態,同時也對應四個動做:投入25分錢
,退回25分錢
,轉動曲柄
和發放糖果
。python
那如何從狀態圖獲得真正的代碼呢?小程序
簡單代碼實現以下:設計模式
#! -*- 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
考慮 封裝變化,把每一個狀態的行爲都放在各自的類中,每一個狀態只要實現本身的動做,用加入新類的方式來實現新狀態的加入。
新的實現代碼以下:測試
#! -*- 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() def turn_crank(self): # 轉動曲柄 self.state.turn_crank() self.state.dispense() 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)
重構後的代碼相對於以前的代碼作了哪些事情呢?spa
狀態類
對修改關閉,對糖果季類對擴展開放
下圖是剛初始狀態圖示:設計
上面重構部分代碼使用的就是狀態模式:code
狀態模式
: 狀態模式容許對象在內部狀態改變時改變它的行爲,對象看起來好像修改了它的類。
狀態模式的類圖以下:協程
狀態模式是將多個行爲封裝在狀態對象中, 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 設計模式》。
最後,感謝女友支持和包容,比❤️
也能夠在公號輸入如下關鍵字獲取歷史文章:公號&小程序
| 設計模式
| 併發&協程