设计模式入门-状态模式(php版)

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

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

一、没有钱的状态

二、有钱的状态

三、售出的状态

四、销售一空的状态

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

JuiceMachine.php

<?php
/**
 * 饮料机
 * @author ben
 *
 */
class JuiceMachine{
	/**
	 * 糖果机一共存在四种状态:没钱,有钱,成功售出以及销售一空
	 * 
	 * 没钱的状态
	 * @var INT
	 */
	const NOMONEY = 0;
	
	/**
	 * 有钱的状态
	 * @var INT
	 */
	const HASMONEY = 1;
	
	/**
	 * 成功售出的状态
	 * @var INT
	 */
	const SOLD = 2;
	
	/**
	 * 销售一空的状态
	 * @var INT
	 */
	const SOLDOUT = 3;
	
	/**
	 * 记录糖果机当前的状态,初始化状态为售空
	 * @var INT
	 */
	private $_state = JuiceMachine::SOLDOUT;
	
	/**
	 * 该变量用于记录饮料机中饮料的数量
	 */
	private $_count; 
	
	/**
	 * 构造方法,最主要是用来初始化count和state属性的
	 */
	public function __construct($count){
	    $this->_count = $count;
	    //当饮料机中的饮料数量大于零时,将饮料机的状态重置为没有钱的状态。
	    if($this->_count > 0){
	        $this->_state = JuiceMachine::NOMONEY;
	    }
	}
	
	/**
	 * 投入硬币
	 */
	public function insertCoin(){
	    if($this->_state == JuiceMachine::HASMONEY ){
	        echo "you can't insert another coin!<br />";
	    }elseif($this->_state == JuiceMachine::NOMONEY){
	        echo "you just insert a coin<br />";
	        $this->_state = JuiceMachine::HASMONEY;
	    }elseif($this->_state == JuiceMachine::SOLD){
	        echo "wait a minute, we are giving you a bottle of juice<br />";
	    }elseif($this->_state == JuiceMachine::SOLDOUT){
	        echo "you can't insert coin, the machine is already soldout<br />";
	    }
	}
	
	/**
	 * 退回硬币
	 */
	public function retreatCoin(){
	    if($this->_state == JuiceMachine::HASMONEY ){
	        echo "coin return!<br />";
	        $this->_state = JuiceMachine::NOMONEY;
	    }elseif($this->_state == JuiceMachine::NOMONEY){
	        echo "you have'nt inserted a coin yet<br />";
	    }elseif($this->_state == JuiceMachine::SOLD){
	        echo "sorry, you already clicked the botton<br />";
	    }elseif($this->_state == JuiceMachine::SOLDOUT){
	        echo "you have'nt inserted a coin yet<br />";
	    }
	}
	
	/**
	 * 点击饮料对应的按钮
	 */
	public function clickButton(){
	    if($this->_state == JuiceMachine::HASMONEY ){
	        echo "you clicked, we are giving you a bottle of juice...<br />";
	        $this->_state = JuiceMachine::SOLD;    //改变饮料机的状态为售出模式
	        $this->dispend();
	    }elseif($this->_state == JuiceMachine::NOMONEY){
	        echo "you clicked,but you hav'nt inserted a coin yet<br />";
	    }elseif($this->_state == JuiceMachine::SOLD){
	        echo "click twice does'nt get you two bottle of juice<br />";
	    }elseif($this->_state == JuiceMachine::SOLDOUT){
	        echo "you clicked, but the machine is already soldout<br />";
	    }
	}
	
	/**
	 * 发放饮料
	 */
	public function dispend(){
	    if($this->_state == JuiceMachine::HASMONEY ){
	        echo "please click the button first<br />";
	    }elseif($this->_state == JuiceMachine::NOMONEY){
	        echo "you need to pay first<br />";
	    }elseif($this->_state == JuiceMachine::SOLD){
	        echo "now you get you juice<br />";
	        //饮料机中的饮料数量减一
	        $this->_count--;
	        if($this->_count <= 0){
	            echo "opps, runing out of juice<br />";
	            //如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
	            $this->_state = JuiceMachine::SOLDOUT;
	        }else{
	            //将饮料机的状态重置为没有钱
	            $this->_state = JuiceMachine::NOMONEY;
	        }
	    }elseif($this->_state == JuiceMachine::SOLDOUT){
	        //其实这种情况不应该出现
	        echo "opps, it appears that we don't have any juice left<br />";
	    }
	}
}

index.php

<?php
require_once 'JuiceMachine.php';

$juiceMachine = new JuiceMachine(1);

$juiceMachine->insertCoin();
$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

<?php
interface State{
    
    /**
     * 插入硬币
     */
    public function insertCoin();
    
    /**
     * 回退硬币
     */
    public function retreatCoin();
    
    /**
     * 点击按钮
     */
    public function clickButton();
    
    /**
     * 发放饮料
     */
    public function dispend();
}

NomoneyState.php

<?php
require_once 'State.php';
class NomoneyState implements State{
    
    /**
     * 饮料机的实例
     * 
     * @var object
     */
    private $_juiceMachine;
    
    /**
     * 构造方法,主要用于初始化饮料机实例
     * 
     */
    public function __construct($juiceMachine){
        $this->_juiceMachine = $juiceMachine;
    }
    
	/* (non-PHPdoc)
     * @see State::insertCoin()
     */
    public function insertCoin()
    {
        // TODO Auto-generated method stub
        echo "you just insert a coin<br />";
        //将饮料机的状态切换成有钱的状态
        $this->_juiceMachine->setState($this->_juiceMachine->getHasmoneyState());
    }

	/* (non-PHPdoc)
     * @see State::retreatCoin()
     */
    public function retreatCoin()
    {
        // TODO Auto-generated method stub
        echo "you have'nt inserted a coin yet<br />";
    }

	/* (non-PHPdoc)
     * @see State::clickButton()
     */
    public function clickButton()
    {
        // TODO Auto-generated method stub
        echo "you clicked,but you hav'nt inserted a coin yet<br />";
    }

	/* (non-PHPdoc)
     * @see State::dispend()
     */
    public function dispend()
    {
        // TODO Auto-generated method stub
        echo "you need to pay first<br />";
    }

    
}
HasmoneyState.php

<?php
require_once 'State.php';

class HasmoneyState implements State
{

    /**
     * 饮料机的实例
     *
     * @var object
     */
    private $_juiceMachine;

    /**
     * 构造方法,主要用于初始化饮料机实例
     */
    public function __construct($juiceMachine)
    {
        $this->_juiceMachine = $juiceMachine;
    }
    
    /*
     * (non-PHPdoc) @see State::insertCoin()
     */
    public function insertCoin()
    {
        // TODO Auto-generated method stub
        echo "you can't insert another coin!<br />";
    }
    
    /*
     * (non-PHPdoc) @see State::retreatCoin()
     */
    public function retreatCoin()
    {
        // TODO Auto-generated method stub
        echo "coin return!<br />";
        $this->_juiceMachine->setState($this->_juiceMachine->getNomoneyState());
    }
    
    /*
     * (non-PHPdoc) @see State::clickButton()
     */
    public function clickButton()
    {
        // TODO Auto-generated method stub
        echo "you clicked, we are giving you a bottle of juice...<br />";
        // 改变饮料机的状态为售出模式
        $rand = mt_rand(0, 0);
        // 当随机数为0(即1/10的概率)并且饮料机中还有1瓶以上的饮料时
        if ($rand == 0 && $this->_juiceMachine->getCount() > 1) {
            $this->_juiceMachine->setState($this->_juiceMachine->getWinnerState());
        } else {
            $this->_juiceMachine->setState($this->_juiceMachine->getSoldState());
        }
    }
    
    /*
     * (non-PHPdoc) @see State::dispend()
     */
    public function dispend()
    {
        // TODO Auto-generated method stub
        echo "please click the button first<br />";
    }
}

SoldoutState.php

<?php
require_once 'State.php';
class SoldoutState implements State{
    
    /**
     * 饮料机的实例
     *
     * @var object
     */
    private $_juiceMachine;
    
    /**
     * 构造方法,主要用于初始化饮料机实例
     *
     */
    public function __construct($juiceMachine){
        $this->_juiceMachine = $juiceMachine;
    }
    
	/* (non-PHPdoc)
     * @see State::insertCoin()
     */
    public function insertCoin()
    {
        // TODO Auto-generated method stub
        echo "you can't insert coin, the machine is already soldout<br />";
    }

	/* (non-PHPdoc)
     * @see State::retreatCoin()
     */
    public function retreatCoin()
    {
        // TODO Auto-generated method stub
        echo "you have'nt inserted a coin yet<br />";
    }

	/* (non-PHPdoc)
     * @see State::clickButton()
     */
    public function clickButton()
    {
        // TODO Auto-generated method stub
        echo "you clicked, but the machine is already soldout<br />";
    }

	/* (non-PHPdoc)
     * @see State::dispend()
     */
    public function dispend()
    {
        // TODO Auto-generated method stub
        echo "opps, it appears that we don't have any juice left<br />";
    }

    
}
SoldState.php

<?php
require_once 'State.php';
class SoldState implements State{
    
    /**
     * 饮料机的实例
     *
     * @var object
     */
    private $_juiceMachine;
    
    /**
     * 构造方法,主要用于初始化饮料机实例
     *
     */
    public function __construct($juiceMachine){
        $this->_juiceMachine = $juiceMachine;
    }
    
	/* (non-PHPdoc)
     * @see State::insertCoin()
     */
    public function insertCoin()
    {
        // TODO Auto-generated method stub
        echo "wait a minute, we are giving you a bottle of juice<br />";
    }

	/* (non-PHPdoc)
     * @see State::retreatCoin()
     */
    public function retreatCoin()
    {
        // TODO Auto-generated method stub
        echo "sorry, you already clicked the botton<br />";
    }

	/* (non-PHPdoc)
     * @see State::clickButton()
     */
    public function clickButton()
    {
        // TODO Auto-generated method stub
        echo "click twice does'nt get you two bottle of juice<br />";
    }

	/* (non-PHPdoc)
     * @see State::dispend()
     */
    public function dispend()
    {
        $this->_juiceMachine->decJuice();
        if($this->_juiceMachine->getCount() <= 0){
            echo "opps, runing out of juice<br />";
            //如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
             $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
        }else{
            //将饮料机的状态重置为没有钱
             $this->_juiceMachine->setState($this->_juiceMachine->getNomoneyState());
        }
    }
    
}
WinnerState.php

<?php
require_once 'State.php';

class WinnerState implements State
{

    /**
     * 饮料机的实例
     *
     * @var object
     */
    private $_juiceMachine;

    /**
     * 构造方法,主要用于初始化饮料机实例
     */
    public function __construct($juiceMachine)
    {
        $this->_juiceMachine = $juiceMachine;
    }
    
    /*
     * (non-PHPdoc) @see State::insertCoin()
     */
    public function insertCoin()
    {
        // TODO Auto-generated method stub
        echo "wait a minute, we are giving you a bottle of juice<br />";
    }
    
    /*
     * (non-PHPdoc) @see State::retreatCoin()
     */
    public function retreatCoin()
    {
        // TODO Auto-generated method stub
        echo "sorry, you already clicked the botton<br />";
    }
    
    /*
     * (non-PHPdoc) @see State::clickButton()
     */
    public function clickButton()
    {
        // TODO Auto-generated method stub
        echo "click twice does'nt get you two bottle of juice<br />";
    }
    
    /*
     * (non-PHPdoc) @see State::dispend()
     */
    public function dispend()
    {
        echo "you are a winner! you get two bottle of juice!<br />";
        $this->_juiceMachine->decJuice();
        if ($this->_juiceMachine->getCount() > 0) {
            $this->_juiceMachine->decJuice();
            if ($this->_juiceMachine->getCount() <= 0) {
                echo "opps, runing out of juice<br />";
                // 如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
                $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
            } else {
                // 将饮料机的状态重置为没有钱
                $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
            }
        } else {
            echo "opps, runing out of juice<br />";
            // 如果这时饮料机中没有饮料了,将饮料机的状态重置为销售一空
            $this->_juiceMachine->setState($this->_juiceMachine->getSoldoutState());
        }
    }
}

JuiceMachine.php

<?php
require_once './state/NomoneyState.php';
require_once './state/HasmoneyState.php';
require_once './state/SoldState.php';
require_once './state/SoldoutState.php';
require_once './state/WinnerState.php';

class JuiceMachine
{

    /**
     * 记录糖果机当前的状态,初始化状态为售空
     * 
     * @var object
     */
    private $_state;

    /**
     * 该变量用于记录饮料机中饮料的数量
     */
    private $_count;

    /**
     * 构造方法,最主要是用来初始化count和state属性的
     */
    public function __construct($count)
    {
        $this->_state = new SoldoutState($this);
        $this->_count = $count;
        // 当饮料机中的饮料数量大于零时,将饮料机的状态重置为没有钱的状态。
        if ($this->_count > 0) {
            $this->_state = new NomoneyState($this);
        }
    }
    
    /*
     * (non-PHPdoc) @see State::insertCoin()
     */
    public function insertCoin()
    {
        // TODO Auto-generated method stub
        $this->_state->insertCoin();
    }
    
    /*
     * (non-PHPdoc) @see State::retreatCoin()
     */
    public function retreatCoin()
    {
        // TODO Auto-generated method stub
        $this->_state->retreatCoin();
    }
    
    /*
     * (non-PHPdoc) @see State::clickButton()
     */
    public function clickButton()
    {
        $this->_state->clickButton();
        //其实发放糖果是在用户点击完按钮后机器内部进行的所有没有必要再写一个dispend方法
        $this->_state->dispend();
    }
    
    /**
     * 设置糖果机的状态
     * 
     * @param State $state
     */
    public function setState(State $state)
    {
        $this->_state = $state;
    }
    
    /**
     * 获取没有钱的状态
     */
    public function getNomoneyState(){
        return new NomoneyState($this);
    }
    
    /**
     * 获取有钱的状态
     */
    public function getHasmoneyState(){
        return new HasmoneyState($this);
    }
    
    /**
     * 获取售出的状态
     */
    public function getSoldState(){
        return new SoldState($this);
    }
    
    /**
     * 获取销售一空的状态
     */
    public function getSoldoutState(){
        return new SoldoutState($this);
    }
    
    /**
     * 获取幸运者的状态
     */
    public function getWinnerState(){
        return new WinnerState($this);
    }
    
    /**
     * 获取饮料机中饮料的数量
     */
    public function getCount(){
        return $this->_count;
    }
    
    /**
     * 将饮料数量减一
     */
    public function decJuice(){
        echo "now you get you juice<br />";
        //饮料机中的饮料数量减一
        $this->_count--;
    }
    
}

index.php

<?php
require_once 'JuiceMachine.php';

$juiceMachine = new JuiceMachine(2);

$juiceMachine->insertCoin();
$juiceMachine->clickButton();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值