资讯专栏INFORMATION COLUMN

python设计模式-状态模式

A Loity / 2321人阅读

摘要:很明显,有有分钱没有分钱售出糖果糖果售罄四个状态,同时也对应四个动作投入分钱,退回分钱,转动曲柄和发放糖果。状态模式的类图如下状态模式是将多个行为封装在状态对象中,的行为随时可委托到其中一个状态中。

</>复制代码

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

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

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

简单代码实现如下:

</>复制代码

  1. #! -*- coding: utf-8 -*-
  2. class GumballMachine:
  3. # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值
  4. STATE_SOLD_OUT = 0
  5. STATE_NO_QUARTER = 1
  6. STATE_HAS_QUARTER = 2
  7. STATE_SOLD = 3
  8. state = STATE_SOLD_OUT
  9. def __init__(self, count=0):
  10. self.count = count
  11. if count > 0:
  12. self.state = self.STATE_NO_QUARTER
  13. def __str__(self):
  14. return "Gumball machine current state: %s" % self.state
  15. def insert_quarter(self):
  16. # 投入25分钱
  17. if self.state == self.STATE_HAS_QUARTER: # 如果已经投过
  18. print("You can"t insert another quarter")
  19. elif self.state == self.STATE_NO_QUARTER: # 如果没有投过
  20. self.state = self.STATE_HAS_QUARTER
  21. print("You inserted a quarter")
  22. elif self.state == self.STATE_SOLD_OUT: # 如果已经售罄
  23. print("You can"t insert a quarter, the machine is sold out")
  24. elif self.state == self.STATE_SOLD: # 如果刚刚买了糖果
  25. print("Please wait, we"re already giving you a gumball")
  26. def eject_quarter(self):
  27. # 退回25分
  28. if self.state == self.STATE_HAS_QUARTER:
  29. print("Quarter returned")
  30. self.state = self.STATE_NO_QUARTER
  31. elif self.state == self.STATE_NO_QUARTER:
  32. print("You haven"t inserted a quarter")
  33. elif self.state == self.STATE_SOLD:
  34. print("Sorry, you alread turned the crank")
  35. elif self.state == self.SOLD_OUT:
  36. print("You can"t eject, you haven"t inserted")
  37. def turn_crank(self):
  38. # 转动曲柄
  39. if self.state == self.STATE_SOLD:
  40. print("Turning twice doesn"t get you another gumball")
  41. elif self.state == self.STATE_NO_QUARTER:
  42. print("You turned but there"s no quarter")
  43. elif self.state == self.STATE_SOLD_OUT:
  44. print("You turned, but there are no gumballs")
  45. elif self.state == self.STATE_HAS_QUARTER:
  46. print("You turned...")
  47. self.state = self.STATE_SOLD
  48. self.dispense()
  49. def dispense(self):
  50. # 发放糖果
  51. if self.state == self.STATE_SOLD:
  52. print("A gumball comes rolling out the slot")
  53. self.count -= 1
  54. if self.count == 0:
  55. self.state = self.STATE_SOLD_OUT
  56. else:
  57. self.state = self.STATE_NO_QUARTER
  58. elif self.state == self.STATE_NO_QUARTER:
  59. print("You need to pay first")
  60. elif self.state == self.STATE_SOLD_OUT:
  61. print("No gumball dispensed")
  62. elif self.state == self.STATE_HAS_QUARTER:
  63. print("No gumball dispensed")
  64. if __name__ == "__main__":
  65. # 以下是代码测试
  66. gumball_machine = GumballMachine(5) # 装入5 个糖果
  67. print(gumball_machine)
  68. gumball_machine.insert_quarter() # 投入25分钱
  69. gumball_machine.turn_crank() # 转动曲柄
  70. print(gumball_machine)
  71. gumball_machine.insert_quarter() #投入25分钱
  72. gumball_machine.eject_quarter() # 退钱
  73. gumball_machine.turn_crank() # 转动曲柄
  74. print(gumball_machine)
  75. gumball_machine.insert_quarter() # 投入25分钱
  76. gumball_machine.turn_crank() # 转动曲柄
  77. gumball_machine.insert_quarter() # 投入25分钱
  78. gumball_machine.turn_crank() # 转动曲柄
  79. gumball_machine.eject_quarter() # 退钱
  80. print(gumball_machine)

这段代码有几个问题:

没有遵守开放-关闭原则

更像是面向过程的设计

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

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

如何改进呢?

</>复制代码

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

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

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

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

新的实现代码如下:

</>复制代码

  1. #! -*- coding: utf-8 -*-
  2. class State:
  3. # 定义state基类
  4. def insert_quarter(self):
  5. pass
  6. def eject_quarter(self):
  7. pass
  8. def turn_crank(self):
  9. pass
  10. def dispense(self):
  11. pass
  12. class SoldOutState(State):
  13. # 继承State 类
  14. def __init__(self, gumball_machine):
  15. self.gumball_machine = gumball_machine
  16. def __str__(self):
  17. return "sold_out"
  18. def insert_quarter(self):
  19. print("You can"t insert a quarter, the machine is sold out")
  20. def eject_quarter(self):
  21. print("You can"t eject, you haven"t inserted a quarter yet")
  22. def turn_crank(self):
  23. print("You turned, but ther are no gumballs")
  24. def dispense(self):
  25. print("No gumball dispensed")
  26. class SoldState(State):
  27. # 继承State 类
  28. def __init__(self, gumball_machine):
  29. self.gumball_machine = gumball_machine
  30. def __str__(self):
  31. return "sold"
  32. def insert_quarter(self):
  33. print("Please wait, we"re already giving you a gumball")
  34. def eject_quarter(self):
  35. print("Sorry, you already turned the crank")
  36. def turn_crank(self):
  37. print("Turning twice doesn"t get you another gumball")
  38. def dispense(self):
  39. self.gumball_machine.release_ball()
  40. if gumball_machine.count > 0:
  41. self.gumball_machine.state = self.gumball_machine.no_quarter_state
  42. else:
  43. print("Oops, out of gumballs!")
  44. self.gumball_machine.state = self.gumball_machine.soldout_state
  45. class NoQuarterState(State):
  46. # 继承State 类
  47. def __init__(self, gumball_machine):
  48. self.gumball_machine = gumball_machine
  49. def __str__(self):
  50. return "no_quarter"
  51. def insert_quarter(self):
  52. # 投币 并且改变状态
  53. print("You inserted a quarter")
  54. self.gumball_machine.state = self.gumball_machine.has_quarter_state
  55. def eject_quarter(self):
  56. print("You haven"t insert a quarter")
  57. def turn_crank(self):
  58. print("You turned, but there"s no quarter")
  59. def dispense(self):
  60. print("You need to pay first")
  61. class HasQuarterState(State):
  62. # 继承State 类
  63. def __init__(self, gumball_machine):
  64. self.gumball_machine = gumball_machine
  65. def __str__(self):
  66. return "has_quarter"
  67. def insert_quarter(self):
  68. print("You can"t insert another quarter")
  69. def eject_quarter(self):
  70. print("Quarter returned")
  71. self.gumball_machine.state = self.gumball_machine.no_quarter_state
  72. def turn_crank(self):
  73. print("You turned...")
  74. self.gumball_machine.state = self.gumball_machine.sold_state
  75. def dispense(self):
  76. print("No gumball dispensed")
  77. class GumballMachine:
  78. def __init__(self, count=0):
  79. self.count = count
  80. # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值
  81. self.soldout_state = SoldOutState(self)
  82. self.no_quarter_state = NoQuarterState(self)
  83. self.has_quarter_state = HasQuarterState(self)
  84. self.sold_state = SoldState(self)
  85. if count > 0:
  86. self.state = self.no_quarter_state
  87. else:
  88. self.state = self.soldout_state
  89. def __str__(self):
  90. return "Gumball machine current state: %s" % self.state
  91. def insert_quarter(self):
  92. # 投入25分钱
  93. self.state.insert_quarter()
  94. def eject_quarter(self):
  95. # 退回25分
  96. self.state.eject_quarter()
  97. def turn_crank(self):
  98. # 转动曲柄
  99. self.state.turn_crank()
  100. self.state.dispense()
  101. def release_ball(self):
  102. # 发放糖果
  103. print("A gumball comes rolling out the slot...")
  104. if self.count > 0:
  105. self.count -= 1
  106. if __name__ == "__main__":
  107. # 以下是代码测试
  108. gumball_machine = GumballMachine(5) # 装入5 个糖果
  109. print(gumball_machine)
  110. gumball_machine.insert_quarter() # 投入25分钱
  111. gumball_machine.turn_crank() # 转动曲柄
  112. print(gumball_machine)
  113. gumball_machine.insert_quarter() #投入25分钱
  114. gumball_machine.eject_quarter() # 退钱
  115. gumball_machine.turn_crank() # 转动曲柄
  116. print(gumball_machine)
  117. gumball_machine.insert_quarter() # 投入25分钱
  118. gumball_machine.turn_crank() # 转动曲柄
  119. gumball_machine.insert_quarter() # 投入25分钱
  120. gumball_machine.turn_crank() # 转动曲柄
  121. gumball_machine.eject_quarter() # 退钱
  122. print(gumball_machine)

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

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

删除if 语句

状态类对修改关闭,对糖果季类对扩展开放

下图是刚初始状态图示:

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

定义

</>复制代码

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

状态模式的类图如下:

</>复制代码

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

</>复制代码

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

</>复制代码

  1. # 添加WinnerState 类,只有dispense 方法不同,可以从SoldState 类继承
  2. class WinnerState(SoldState):
  3. def __str__(self):
  4. return "winner"
  5. def dispense(self):
  6. print("You"re a WINNER! You get two gumballs for your quarter")
  7. self.gumball_machine.release_ball()
  8. if gumball_machine.count == 0:
  9. self.gumball_machine.state = self.gumball_machine.soldout_state
  10. else:
  11. self.gumball_machine.release_ball()
  12. if gumball_machine.count > 0:
  13. self.gumball_machine.state = self.gumball_machine.no_quarter_state
  14. else:
  15. print("Oops, out of gumballs!")
  16. self.gumball_machine.state = self.gumball_machine.soldout_state
  17. # 修改turn_crank 方法
  18. class HasQuarterState(State):
  19. ...
  20. def turn_crank(self):
  21. print("You turned...")
  22. winner = random.randint(0, 9)
  23. if winner == 4 and self.gumball_machine.count > 1: # 如果库存大于 1 并且随机数等于4(可以是0到9任意值)
  24. self.gumball_machine.state = self.gumball_machine.winner_state
  25. else:
  26. self.gumball_machine.state = self.gumball_machine.sold_state
  27. # 在 GumballMachine 中初始化
  28. class GumballMachine:
  29. def __init__(self, count=0):
  30. self.count = count
  31. # 找出所有状态,并创建实例变量来持有当前状态,然后定义状态的值
  32. ...
  33. self.winner_state = WinnerState(self)
  34. ...
总结

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

状态模式用类代表状态

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

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

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

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

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


</>复制代码

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

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42910.html

相关文章

  • python设计模式-观察者模式

    摘要:在程序设计中,观察者模式通常被定义为观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态是,它的所有依赖者都会收到通知并自动更新。事件扮演发布者的角色,监听者则扮演观察者的角色。 题目:现在你有一个数字,默认格式化程序是以十进制格式展示此数值,但需要提供一个功能,这个程序要支持添加/注册更多的格式化程序(比如:添加一个十六进制格式化程序和一个二进制格式化程序)。每次数值...

    terasum 评论0 收藏0
  • 设计模式手册之状态模式

    摘要:什么是状态模式状态模式对象行为是基于状态来改变的。原文地址设计模式手册之状态模式优缺点优点封装了转化规则,对于大量分支语句,可以考虑使用状态类进一步封装。 1. 什么是状态模式? 状态模式:对象行为是基于状态来改变的。 内部的状态转化,导致了行为表现形式不同。所以,用户在外面看起来,好像是修改了行为。 Webpack4系列教程(17篇) + 设计模式手册(16篇):GitHub地址 博...

    call_me_R 评论0 收藏0
  • 设计模式手册之状态模式

    摘要:什么是状态模式状态模式对象行为是基于状态来改变的。原文地址设计模式手册之状态模式优缺点优点封装了转化规则,对于大量分支语句,可以考虑使用状态类进一步封装。 1. 什么是状态模式? 状态模式:对象行为是基于状态来改变的。 内部的状态转化,导致了行为表现形式不同。所以,用户在外面看起来,好像是修改了行为。 Webpack4系列教程(17篇) + 设计模式手册(16篇):GitHub地址 博...

    Faremax 评论0 收藏0
  • Python学习之路25-使用一等函数实现设计模式

    摘要:本篇主要讲述中使用函数来实现策略模式和命令模式,最后总结出这种做法背后的思想。 《流畅的Python》笔记。本篇主要讲述Python中使用函数来实现策略模式和命令模式,最后总结出这种做法背后的思想。 1. 重构策略模式 策略模式如果用面向对象的思想来简单解释的话,其实就是多态。父类指向子类,根据子类对同一方法的不同重写,得到不同结果。 1.1 经典的策略模式 下图是经典的策略模式的U...

    econi 评论0 收藏0
  • Python 中的进程、线程、协程、同步、异步、回调

    摘要:进程和线程究竟是什么东西传统网络服务模型是如何工作的协程和线程的关系和区别有哪些过程在什么时间发生在刚刚结束的上海站,来自七牛云存储的高级工程师许智翔带来了关于的分享中的进程线程协程同步异步回调。使用红黑树管理就绪队列。 进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 在刚刚结束的 PyCon2014 上海站,来自七牛云...

    Forest10 评论0 收藏0
  • 每天一个设计模式之享元模式

    摘要:作者按每天一个设计模式旨在初步领会设计模式的精髓,目前采用和两种语言实现。享元模式提醒我们将一个对象的属性划分为内部和外部状态。 作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :) 个人技术博客-godbmw.com 欢迎来玩! 每周至少 1 篇原创...

    jone5679 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<