首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

python设计模式-状态模式

有一个糖果公司需要设计一个糖果售卖机,控制流程如下图,需要怎么实现?

这是一个状态图,每个圆圈都是一种状态。很明显,有、、、四个状态,同时也对应四个动作:,,和。那如何从状态图得到真正的代码呢?简单代码实现如下:

#! -*- coding: utf-8 -*-

classGumballMachine:

# 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值

STATE_SOLD_OUT=

STATE_NO_QUARTER=1

STATE_HAS_QUARTER=2

STATE_SOLD=3

state=STATE_SOLD_OUT

def__init__(self,count=):

self.count=count

ifcount>:

self.state=self.STATE_NO_QUARTER

def__str__(self):

return"Gumball machine current state: %s"%self.state

definsert_quarter(self):

# 投入25分钱

ifself.state==self.STATE_HAS_QUARTER:# 如果已经投过

print("You can't insert another quarter")

elifself.state==self.STATE_NO_QUARTER:# 如果没有投过

self.state=self.STATE_HAS_QUARTER

print("You inserted a quarter")

elifself.state==self.STATE_SOLD_OUT:# 如果已经售罄

print("You can't insert a quarter, the machine is sold out")

elifself.state==self.STATE_SOLD:# 如果刚刚买了糖果

print("Please wait, we're already giving you a gumball")

defeject_quarter(self):

# 退回25分

ifself.state==self.STATE_HAS_QUARTER:

print("Quarter returned")

self.state=self.STATE_NO_QUARTER

elifself.state==self.STATE_NO_QUARTER:

print("You haven't inserted a quarter")

elifself.state==self.STATE_SOLD:

print("Sorry, you alread turned the crank")

elifself.state==self.SOLD_OUT:

print("You can't eject, you haven't inserted")

defturn_crank(self):

# 转动曲柄

ifself.state==self.STATE_SOLD:

print("Turning twice doesn't get you another gumball")

elifself.state==self.STATE_NO_QUARTER:

print("You turned but there's no quarter")

elifself.state==self.STATE_SOLD_OUT:

print("You turned, but there are no gumballs")

elifself.state==self.STATE_HAS_QUARTER:

print("You turned...")

self.state=self.STATE_SOLD

self.dispense()

defdispense(self):

# 发放糖果

ifself.state==self.STATE_SOLD:

print("A gumball comes rolling out the slot")

self.count-=1

ifself.count==:

self.state=self.STATE_SOLD_OUT

else:

self.state=self.STATE_NO_QUARTER

elifself.state==self.STATE_NO_QUARTER:

print("You need to pay first")

elifself.state==self.STATE_SOLD_OUT:

print("No gumball dispensed")

elifself.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)

这段代码有几个问题:

没有遵守开放-关闭原则

更像是面向过程的设计

状态转化被埋藏在条件语句中

未来加入新的需求,需要改动的较多,不易维护,可能会出bug

如何改进呢?考虑封装变化,把每个状态的行为都放在各自的类中,每个状态只要实现自己的动作,用加入新类的方式来实现新状态的加入。

定义State 父类,在这个类中,糖果机的每个动作都有一个应对的方法

为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为

摆脱旧的条件代码,将动作委托到状态类

新的实现代码如下:

#! -*- coding: utf-8 -*-

classState:

# 定义state基类

definsert_quarter(self):

pass

defeject_quarter(self):

pass

defturn_crank(self):

pass

defdispense(self):

pass

classSoldOutState(State):

# 继承State 类

def__init__(self,gumball_machine):

self.gumball_machine=gumball_machine

def__str__(self):

return"sold_out"

definsert_quarter(self):

print("You can't insert a quarter, the machine is sold out")

defeject_quarter(self):

print("You can't eject, you haven't inserted a quarter yet")

defturn_crank(self):

print("You turned, but ther are no gumballs")

defdispense(self):

print("No gumball dispensed")

classSoldState(State):

# 继承State 类

def__init__(self,gumball_machine):

self.gumball_machine=gumball_machine

def__str__(self):

return"sold"

definsert_quarter(self):

print("Please wait, we're already giving you a gumball")

defeject_quarter(self):

print("Sorry, you already turned the crank")

defturn_crank(self):

print("Turning twice doesn't get you another gumball")

defdispense(self):

self.gumball_machine.release_ball()

ifgumball_machine.count>:

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

classNoQuarterState(State):

# 继承State 类

def__init__(self,gumball_machine):

self.gumball_machine=gumball_machine

def__str__(self):

return"no_quarter"

definsert_quarter(self):

# 投币 并且改变状态

print("You inserted a quarter")

self.gumball_machine.state=self.gumball_machine.has_quarter_state

defeject_quarter(self):

print("You haven't insert a quarter")

defturn_crank(self):

print("You turned, but there's no quarter")

defdispense(self):

print("You need to pay first")

classHasQuarterState(State):

# 继承State 类

def__init__(self,gumball_machine):

self.gumball_machine=gumball_machine

def__str__(self):

return"has_quarter"

definsert_quarter(self):

print("You can't insert another quarter")

defeject_quarter(self):

print("Quarter returned")

self.gumball_machine.state=self.gumball_machine.no_quarter_state

defturn_crank(self):

print("You turned...")

self.gumball_machine.state=self.gumball_machine.sold_state

defdispense(self):

print("No gumball dispensed")

classGumballMachine:

def__init__(self,count=):

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)

ifcount>:

self.state=self.no_quarter_state

else:

self.state=self.soldout_state

def__str__(self):

return">>> Gumball machine current state: %s"%self.state

definsert_quarter(self):

# 投入25分钱

self.state.insert_quarter()

defeject_quarter(self):

# 退回25分

self.state.eject_quarter()

# print("state", self.state, type(self.state))

defturn_crank(self):

# 转动曲柄

# print("state", self.state, type(self.state))

self.state.turn_crank()

defrelease_ball(self):

# 发放糖果

print("A gumball comes rolling out the slot...")

ifself.count>:

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)

重构后的代码相对于之前的代码做了哪些事情呢?

将每个状态的行为局部话到自己的类中

删除if 语句

将对修改关闭,对糖果季类对

下图是刚初始状态图示:

上面重构部分代码使用的就是状态模式:

定义

: 状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。状态模式的类图如下:

状态模式是将多个行为封装在状态对象中, context 的行为随时可委托到其中一个状态中。当前状态在不同的状态对象中改变,以反映出context 内部的状态,context 的行为也会随之改变。

扩展

如果,现在要在这四个状态的基础上再加一个状态(购买糖果后,有10%的概率再得一个),该如何实现呢?

# 添加WinnerState 类,只有dispense 方法不同,可以从SoldState 类继承

classWinnerState(SoldState):

def__str__(self):

return"winner"

defdispense(self):

print("You're a WINNER! You get two gumballs for your quarter")

self.gumball_machine.release_ball()

ifgumball_machine.count==:

self.gumball_machine.state=self.gumball_machine.soldout_state

else:

self.gumball_machine.release_ball()

ifgumball_machine.count>:

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 方法

classHasQuarterState(State):

...

defturn_crank(self):

print("You turned...")

winner=random.randint(,9)

ifwinner==4andself.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 中初始化

classGumballMachine:

def__init__(self,count=):

self.count=count

# 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值

...

self.winner_state=WinnerState(self)

...

总结

状态模式允许一个对象给予内部状态而拥有不同的行为

状态模式用类代表状态

Context 会将行为委托给当前状态对象

通过将每状态封装进一个类,把改变局部化

状态装欢可以由State 类或Context 类控制

使用状态模式会增加类的数目

状态类可以被多个Context 实例共享

元旦快乐!

本文例子来自《Head First 设计模式》。最后,感谢女朋友支持和包容,比❤️也可以在公号输入以下关键字获取历史文章:||

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181231G12M8Q00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券