博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python设计模式-状态模式
阅读量:5908 次
发布时间:2019-06-19

本文共 9526 字,大约阅读时间需要 31 分钟。

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

1b0d0134acf9ab9b2240066f847412f1.png

这是一个状态图,每个圆圈都是一种状态。很明显,有有25分钱没有25分钱售出糖果糖果售罄四个状态,同时也对应四个动作投入25分钱退回25分钱转动曲柄发放糖果

那如何从状态图得到真正的代码呢?

简单代码实现如下:

#! -*- 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)

这段代码有几个问题:

  1. 没有遵守开放-关闭原则
  2. 更像是面向过程的设计
  3. 状态转化被埋藏在条件语句中
  4. 未来加入新的需求,需要改动的较多,不易维护,可能会出bug

如何改进呢?

考虑
封装变化,把每个状态的行为都放在各自的类中,每个状态只要实现自己的动作,用加入新类的方式来实现新状态的加入。
  1. 定义State 父类,在这个类中,糖果机的每个动作都有一个应对的方法
  2. 为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为
  3. 摆脱旧的条件代码,将动作委托到状态类

新的实现代码如下:

#! -*- coding: utf-8 -*-class State:    # 定义state基类    def insert_quarter(self):        pass    def eject_quarter(self):        pass    def turn_crank(self):        pass    def dispense(self):        passclass 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_stateclass 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)

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

  1. 将每个状态的行为局部话到自己的类中
  2. 删除if 语句
  3. 状态类对修改关闭,对糖果季类对扩展开放

下图是刚初始状态图示:

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

定义

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

状态模式的类图如下:

状态模式是将多个行为封装在状态对象中, 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)        ...

总结

  1. 状态模式允许一个对象给予内部状态而拥有不同的行为
  2. 状态模式用类代表状态
  3. Context 会将行为委托给当前状态对象
  4. 通过将每状态封装进一个类,把改变局部化
  5. 状态装欢可以由State 类或Context 类控制
  6. 使用状态模式会增加类的数目
  7. 状态类可以被多个Context 实例共享

本文例子来自《Head First 设计模式》。

最后,感谢女朋友支持和包容,比❤️

也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程

转载地址:http://lqvpx.baihongyu.com/

你可能感兴趣的文章
spring同时集成mybatis和ibatis
查看>>
IRC 聊天工具(xchat,chatzilla,pidgin)入门教程
查看>>
fixed 计算位置
查看>>
API自动化测试利器——Postman
查看>>
Redis配置
查看>>
DO280中OpenShift的镜像管理
查看>>
kafka 监控之Mx4jLoader
查看>>
wireshark windows 编译
查看>>
RidolIDE+TPM安装运行和Ridol常见bug处理(迅捷版)
查看>>
如何交叉编译 移植 QT
查看>>
模板特化疑问
查看>>
赵海峰:站在电商平台上的互联网金融架构实践
查看>>
-webkit-animation- 实践
查看>>
滚动的标签
查看>>
XBImageFilters
查看>>
观察者模式
查看>>
chpter11~函数和函数式编程
查看>>
Failed to run the WC DB work queue associated with
查看>>
分享一些程序员的办公桌
查看>>
第二章 jQuery选择器
查看>>