PHP设计模式入门之状态模式原理与实现方法分析

本文实例讲述了PHP设计模式入门之状态模式原理与实现方法,分享给大家供大家参考,具体如下:

想必大家都用过自动售卖的自动饮料机吧,塞入硬币或纸币,选择想要的饮料,饮料就会在机器的下方滚出。大家有没有相关如果用程序去写一个饮料机要怎么样实现呢?

首先我们可以分享一下这部饮料机有几种状态

一、没有钱的状态

二、有钱的状态

三、售出的状态

四、销售一空的状态

好吧,知道了这些状态之后我们开始写代码了!

JuiceMachine.php

  1. <?php
  2. /**
  3. * 饮料机
  4. * @author ben
  5. *
  6. */
  7. class JuiceMachine{
  8. /**
  9. * 糖果机一共存在四种状态:没钱,有钱,成功售出以及销售一空
  10. *
  11. * 没钱的状态
  12. * @var INT
  13. */
  14. const NOMONEY = 0;
  15. /**
  16. * 有钱的状态
  17. * @var INT
  18. */
  19. const HASMONEY = 1;
  20. /**
  21. * 成功售出的状态
  22. * @var INT
  23. */
  24. const SOLD = 2;
  25. /**
  26. * 销售一空的状态
  27. * @var INT
  28. */
  29. const SOLDOUT = 3;
  30. /**
  31. * 记录糖果机当前的状态,初始化状态为售空
  32. * @var INT
  33. */
  34. private $_state = JuiceMachine::SOLDOUT;
  35. /**
  36. * 该变量用于记录饮料机中饮料的数量
  37. */
  38. private $_count;
  39. /**
  40. * 构造方法,最主要是用来初始化count和state属性的
  41. */
  42. public function __construct($count){
  43. $this->_count = $count;
  44. //当饮料机中的饮料数量大于零时,将饮料机的状态重置为没有钱的状态。
  45. if($this->_count > 0){
  46. $this->_state = JuiceMachine::NOMONEY;
  47. }
  48. }
  49. /**
  50. * 投入硬币
  51. */
  52. public function insertCoin(){
  53. if($this->_state == JuiceMachine::HASMONEY ){
  54. echo "you can't insert another coin!<br />";
  55. }elseif($this->_state == JuiceMachine::NOMONEY){
  56. echo "you just insert a coin<br />";
  57. $this->_state = JuiceMachine::HASMONEY;
  58. }elseif($this->_state == JuiceMachine::SOLD){
  59. echo "wait a minute, we are giving you a bottle of juice<br />";
  60. }elseif($this->_state == JuiceMachine::SOLDOUT){
  61. echo "you can't insert coin, the machine is already soldout<br />";
  62. }
  63. }
  64. /**
  65. * 退回硬币
  66. */
  67. public function retreatCoin(){
  68. if($this->_state == JuiceMachine::HASMONEY ){
  69. echo "coin return!<br />";
  70. $this->_state = JuiceMachine::NOMONEY;
  71. }elseif($this->_state == JuiceMachine::NOMONEY){
  72. echo "you have'nt inserted a coin yet<br />";
  73. }elseif($this->_state == JuiceMachine::SOLD){
  74. echo "sorry, you already clicked the botton<br />";
  75. }elseif($this->_state == JuiceMachine::SOLDOUT){
  76. echo "you have'nt inserted a coin yet<br />";
  77. }
  78. }
  79. /**
  80. * 点击饮料对应的按钮
  81. */
  82. public function clickButton(){
  83. if($this->_state == JuiceMachine::HASMONEY ){
  84. echo "you clicked, we are giving you a bottle of juice...<br />";
  85. $this->_state = JuiceMachine::SOLD; //改变饮料机的状态为售出模式
  86. $this->dispend();
  87. }elseif($this->_state == JuiceMachine::NOMONEY){
  88. echo "you clicked,but you hav'nt inserted a coin yet<br />";
  89. }elseif($this->_state == JuiceMachine::SOLD){
  90. echo "click twice does'nt get you two bottle of juice<br />";
  91. }elseif($this->_state == JuiceMachine::SOLDOUT){
  92. echo "you clicked, but the machine is already soldout<br />";
  93. }
  94. }
  95. /**
  96. * 发放饮料
  97. */
  98. public function dispend(){
  99. if($this->_state == JuiceMachine::HASMONEY ){
  100. echo "please click the button first<br />";
  101. }elseif($this->_state == JuiceMachine::NOMONEY){
  102. echo "you need to pay first<br />";
  103. }elseif($this->_state == JuiceMachine::SOLD){
  104. echo "now you get you juice<br />";
  105. //饮料机中的饮料数量减一
  106. $this->_count--;
  107. if($this->_count <= 0){
  108. echo "opps, runing out of juice<br />";
  109. //如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
  110. $this->_state = JuiceMachine::SOLDOUT;
  111. }else{
  112. //将饮料机的状态重置为没有钱
  113. $this->_state = JuiceMachine::NOMONEY;
  114. }
  115. }elseif($this->_state == JuiceMachine::SOLDOUT){
  116. //其实这种情况不应该出现
  117. echo "opps, it appears that we don't have any juice left<br />";
  118. }
  119. }
  120. }

index.php

  1. <?php
  2. require_once 'JuiceMachine.php';
  3. $juiceMachine = new JuiceMachine(1);
  4. $juiceMachine->insertCoin();
  5. $juiceMachine->clickButton();

运行的结果是:

you just insert a coin

you clicked, we are giving you a bottle of juice...

now you get you juice

opps, runing out of juice

到目前为止我们的程序运行良好,没有出现什么问题,但是从这些多重的if判断中你是否嗅到了坏代码的味道呢?有一天问题终于出现了,老板希望当用户点击按钮时有10%的概率拿到两瓶饮料,我们需要为饮料机多加一个状态,这时去修改代码就成为了一种灾难,而且很可能会影响到之前的代码,带来新的bug,看看状态模式如何帮助我们度过难关吧!

状态模式的官方定义是:状态模式允许对象在内部状态改变是改变它的行为,对象看起来好像是修改了它的类

用uml类图表示如下:

在我们这个项目中的实际类图如下:

具体实现代码:

State.php

  1. <?php
  2. interface State{
  3. /**
  4. * 插入硬币
  5. */
  6. public function insertCoin();
  7. /**
  8. * 回退硬币
  9. */
  10. public function retreatCoin();
  11. /**
  12. * 点击按钮
  13. */
  14. public function clickButton();
  15. /**
  16. * 发放饮料
  17. */
  18. public function dispend();
  19. }

NomoneyState.php

  1. <?php
  2. require_once 'State.php';
  3. class NomoneyState implements State{
  4. /**
  5. * 饮料机的实例
  6. *
  7. * @var object
  8. */
  9. private $_juiceMachine;
  10. /**
  11. * 构造方法,主要用于初始化饮料机实例
  12. *
  13. */
  14. public function __construct($juiceMachine){
  15. $this->_juiceMachine = $juiceMachine;
  16. }
  17. /* (non-PHPdoc)
  18. * @see State::insertCoin()
  19. */
  20. public function insertCoin()
  21. {
  22. // TODO Auto-generated method stub
  23. echo "you just insert a coin<br />";
  24. //将饮料机的状态切换成有钱的状态
  25. $this->_juiceMachine->setState($this->_juiceMachine->getHasmoneyState());
  26. }
  27. /* (non-PHPdoc)
  28. * @see State::retreatCoin()
  29. */
  30. public function retreatCoin()
  31. {
  32. // TODO Auto-generated method stub
  33. echo "you have'nt inserted a coin yet<br />";
  34. }
  35. /* (non-PHPdoc)
  36. * @see State::clickButton()
  37. */
  38. public function clickButton()
  39. {
  40. // TODO Auto-generated method stub
  41. echo "you clicked,but you hav'nt inserted a coin yet<br />";
  42. }
  43. /* (non-PHPdoc)
  44. * @see State::dispend()
  45. */
  46. public function dispend()
  47. {
  48. // TODO Auto-generated method stub
  49. echo "you need to pay first<br />";
  50. }
  51. }

HasmoneyState.php

  1. <?php
  2. require_once 'State.php';
  3. class HasmoneyState implements State
  4. {
  5. /**
  6. * 饮料机的实例
  7. *
  8. * @var object
  9. */
  10. private $_juiceMachine;
  11. /**
  12. * 构造方法,主要用于初始化饮料机实例
  13. */
  14. public function __construct($juiceMachine)
  15. {
  16. $this->_juiceMachine = $juiceMachine;
  17. }
  18. /*
  19. * (non-PHPdoc) @see State::insertCoin()
  20. */
  21. public function insertCoin()
  22. {
  23. // TODO Auto-generated method stub
  24. echo "you can't insert another coin!<br />";
  25. }
  26. /*
  27. * (non-PHPdoc) @see State::retreatCoin()
  28. */
  29. public function retreatCoin()
  30. {
  31. // TODO Auto-generated method stub
  32. echo "coin return!<br />";
  33. $this->_juiceMachine->setState($this->_juiceMachine->getNomoneyState());
  34. }
  35. /*
  36. * (non-PHPdoc) @see State::clickButton()
  37. */
  38. public function clickButton()
  39. {
  40. // TODO Auto-generated method stub
  41. echo "you clicked, we are giving you a bottle of juice...<br />";
  42. // 改变饮料机的状态为售出模式
  43. $rand = mt_rand(0, 0);
  44. // 当随机数为0(即1/10的概率)并且饮料机中还有1瓶以上的饮料时
  45. if ($rand == 0 && $this->_juiceMachine->getCount() > 1) {
  46. $this->_juiceMachine->setState($this->_juiceMachine->getWinnerState());
  47. } else {
  48. $this->_juiceMachine->setState($this->_juiceMachine->getSoldState());
  49. }
  50. }
  51. /*
  52. * (non-PHPdoc) @see State::dispend()
  53. */
  54. public function dispend()
  55. {
  56. // TODO Auto-generated method stub
  57. echo "please click the button first<br />";
  58. }
  59. }

SoldoutState.php

  1. <?php
  2. require_once 'State.php';
  3. class SoldoutState implements State{
  4. /**
  5. * 饮料机的实例
  6. *
  7. * @var object
  8. */
  9. private $_juiceMachine;
  10. /**
  11. * 构造方法,主要用于初始化饮料机实例
  12. *
  13. */
  14. public function __construct($juiceMachine){
  15. $this->_juiceMachine = $juiceMachine;
  16. }
  17. /* (non-PHPdoc)
  18. * @see State::insertCoin()
  19. */
  20. public function insertCoin()
  21. {
  22. // TODO Auto-generated method stub
  23. echo "you can't insert coin, the machine is already soldout<br />";
  24. }
  25. /* (non-PHPdoc)
  26. * @see State::retreatCoin()
  27. */
  28. public function retreatCoin()
  29. {
  30. // TODO Auto-generated method stub
  31. echo "you have'nt inserted a coin yet<br />";
  32. }
  33. /* (non-PHPdoc)
  34. * @see State::clickButton()
  35. */
  36. public function clickButton()
  37. {
  38. // TODO Auto-generated method stub
  39. echo "you clicked, but the machine is already soldout<br />";
  40. }
  41. /* (non-PHPdoc)
  42. * @see State::dispend()
  43. */
  44. public function dispend()
  45. {
  46. // TODO Auto-generated method stub
  47. echo "opps, it appears that we don't have any juice left<br />";
  48. }
  49. }

SoldState.php

  1. <?php
  2. require_once 'State.php';
  3. class SoldState implements State{
  4. /**
  5. * 饮料机的实例
  6. *
  7. * @var object
  8. */
  9. private $_juiceMachine;
  10. /**
  11. * 构造方法,主要用于初始化饮料机实例
  12. *
  13. */
  14. public function __construct($juiceMachine){
  15. $this->_juiceMachine = $juiceMachine;
  16. }
  17. /* (non-PHPdoc)
  18. * @see State::insertCoin()
  19. */
  20. public function insertCoin()
  21. {
  22. // TODO Auto-generated method stub
  23. echo "wait a minute, we are giving you a bottle of juice<br />";
  24. }
  25. /* (non-PHPdoc)
  26. * @see State::retreatCoin()
  27. */
  28. public function retreatCoin()
  29. {
  30. // TODO Auto-generated method stub
  31. echo "sorry, you already clicked the botton<br />";
  32. }
  33. /* (non-PHPdoc)
  34. * @see State::clickButton()
  35. */
  36. public function clickButton()
  37. {
  38. // TODO Auto-generated method stub
  39. echo "click twice does'nt get you two bottle of juice<br />";
  40. }
  41. /* (non-PHPdoc)
  42. * @see State::dispend()
  43. */
  44. public function dispend()
  45. {
  46. $this->_juiceMachine->decJuice();
  47. if($this->_juiceMachine->getCount() <= 0){
  48. echo "opps, runing out of juice<br />";
  49. //如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
  50. $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
  51. }else{
  52. //将饮料机的状态重置为没有钱
  53. $this->_juiceMachine->setState($this->_juiceMachine->getNomoneyState());
  54. }
  55. }
  56. }

WinnerState.php

  1. <?php
  2. require_once 'State.php';
  3. class WinnerState implements State
  4. {
  5. /**
  6. * 饮料机的实例
  7. *
  8. * @var object
  9. */
  10. private $_juiceMachine;
  11. /**
  12. * 构造方法,主要用于初始化饮料机实例
  13. */
  14. public function __construct($juiceMachine)
  15. {
  16. $this->_juiceMachine = $juiceMachine;
  17. }
  18. /*
  19. * (non-PHPdoc) @see State::insertCoin()
  20. */
  21. public function insertCoin()
  22. {
  23. // TODO Auto-generated method stub
  24. echo "wait a minute, we are giving you a bottle of juice<br />";
  25. }
  26. /*
  27. * (non-PHPdoc) @see State::retreatCoin()
  28. */
  29. public function retreatCoin()
  30. {
  31. // TODO Auto-generated method stub
  32. echo "sorry, you already clicked the botton<br />";
  33. }
  34. /*
  35. * (non-PHPdoc) @see State::clickButton()
  36. */
  37. public function clickButton()
  38. {
  39. // TODO Auto-generated method stub
  40. echo "click twice does'nt get you two bottle of juice<br />";
  41. }
  42. /*
  43. * (non-PHPdoc) @see State::dispend()
  44. */
  45. public function dispend()
  46. {
  47. echo "you are a winner! you get two bottle of juice!<br />";
  48. $this->_juiceMachine->decJuice();
  49. if ($this->_juiceMachine->getCount() > 0) {
  50. $this->_juiceMachine->decJuice();
  51. if ($this->_juiceMachine->getCount() <= 0) {
  52. echo "opps, runing out of juice<br />";
  53. // 如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
  54. $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
  55. } else {
  56. // 将饮料机的状态重置为没有钱
  57. $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
  58. }
  59. } else {
  60. echo "opps, runing out of juice<br />";
  61. // 如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
  62. $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
  63. }
  64. }
  65. }

JuiceMachine.php

  1. <?php
  2. require_once './state/NomoneyState.php';
  3. require_once './state/HasmoneyState.php';
  4. require_once './state/SoldState.php';
  5. require_once './state/SoldoutState.php';
  6. require_once './state/WinnerState.php';
  7. class JuiceMachine
  8. {
  9. /**
  10. * 记录糖果机当前的状态,初始化状态为售空
  11. *
  12. * @var object
  13. */
  14. private $_state;
  15. /**
  16. * 该变量用于记录饮料机中饮料的数量
  17. */
  18. private $_count;
  19. /**
  20. * 构造方法,最主要是用来初始化count和state属性的
  21. */
  22. public function __construct($count)
  23. {
  24. $this->_state = new SoldoutState($this);
  25. $this->_count = $count;
  26. // 当饮料机中的饮料数量大于零时,将饮料机的状态重置为没有钱的状态。
  27. if ($this->_count > 0) {
  28. $this->_state = new NomoneyState($this);
  29. }
  30. }
  31. /*
  32. * (non-PHPdoc) @see State::insertCoin()
  33. */
  34. public function insertCoin()
  35. {
  36. // TODO Auto-generated method stub
  37. $this->_state->insertCoin();
  38. }
  39. /*
  40. * (non-PHPdoc) @see State::retreatCoin()
  41. */
  42. public function retreatCoin()
  43. {
  44. // TODO Auto-generated method stub
  45. $this->_state->retreatCoin();
  46. }
  47. /*
  48. * (non-PHPdoc) @see State::clickButton()
  49. */
  50. public function clickButton()
  51. {
  52. $this->_state->clickButton();
  53. //其实发放糖果是在用户点击完按钮后机器内部进行的所有没有必要再写一个dispend方法
  54. $this->_state->dispend();
  55. }
  56. /**
  57. * 设置糖果机的状态
  58. *
  59. * @param State $state
  60. */
  61. public function setState(State $state)
  62. {
  63. $this->_state = $state;
  64. }
  65. /**
  66. * 获取没有钱的状态
  67. */
  68. public function getNomoneyState(){
  69. return new NomoneyState($this);
  70. }
  71. /**
  72. * 获取有钱的状态
  73. */
  74. public function getHasmoneyState(){
  75. return new HasmoneyState($this);
  76. }
  77. /**
  78. * 获取售出的状态
  79. */
  80. public function getSoldState(){
  81. return new SoldState($this);
  82. }
  83. /**
  84. * 获取销售一空的状态
  85. */
  86. public function getSoldoutState(){
  87. return new SoldoutState($this);
  88. }
  89. /**
  90. * 获取幸运者的状态
  91. */
  92. public function getWinnerState(){
  93. return new WinnerState($this);
  94. }
  95. /**
  96. * 获取饮料机中饮料的数量
  97. */
  98. public function getCount(){
  99. return $this->_count;
  100. }
  101. /**
  102. * 将饮料数量减一
  103. */
  104. public function decJuice(){
  105. echo "now you get you juice<br />";
  106. //饮料机中的饮料数量减一
  107. $this->_count--;
  108. }
  109. }

index.php

  1. <?php
  2. require_once 'JuiceMachine.php';
  3. $juiceMachine = new JuiceMachine(2);
  4. $juiceMachine->insertCoin();
  5. $juiceMachine->clickButton();