state Состояние (англ. State) — поведенческий шаблон проектирования. Инкапсулирует изменение поведения одних и тех же методов в зависимости от состояния объекта. Этот шаблон поможет изящным способом изменить поведение объекта во время выполнения, не прибегая к большим монолитным условным операторам. Извне создаётся впечатление, что изменился класс объекта.

  • States
    • AbstractState.php
                    
      <?php
      
      namespace DesignPatterns\Behavioral\State\States;
      
      use DesignPatterns\Behavioral\State\Glass;
      use Exception;
      
      /**
       * Class AbstractState
       * @package DesignPatterns\Behavioral\State\States
       */
      abstract class AbstractState implements StateInterface
      {
          const STATE = null;
      
          /**
           * @var Glass
           */
          protected $glass;
      
          /**
           * AbstractState constructor.
           *
           * @param Glass $glass
           */
          public function __construct(Glass $glass)
          {
              $this->glass = $glass;
          }
      
          /**
           * @return string
           */
          protected function getGlassState(): string
          {
              $glassState = $this->glass->getState();
      
              return $glassState::STATE;
          }
      
          /**
           * @return FillState
           *
           * @throws Exception
           */
          abstract public function onFilled(): FillState;
      
          /**
           * @return EmptyState
           *
           * @throws Exception
           */
          abstract public function onEmpty(): EmptyState;
      }
      
                    
    • EmptyState.php
                    
      <?php
      
      namespace DesignPatterns\Behavioral\State\States;
      
      use Exception;
      
      /**
       * Class EmptyState
       * @package DesignPatterns\Behavioral\State\States
       */
      class EmptyState extends AbstractState
      {
          const STATE = 'Empty';
      
          /**
           * @return FillState
           *
           * @throws Exception
           */
          public function onFilled(): FillState
          {
              if (static::STATE === $this->getGlassState()) {
                  return new FillState($this->glass);
              }
      
              throw new Exception('Can\'t to fill filled glass.');
          }
      
          /**
           * @return EmptyState
           *
           * @throws Exception
           */
          public function onEmpty(): EmptyState
          {
              throw new Exception('Сan\'t pour the empty glass.');
          }
      }
      
                    
    • FillState.php
                    
      <?php
      
      namespace DesignPatterns\Behavioral\State\States;
      
      use Exception;
      
      /**
       * Class FillState
       * @package DesignPatterns\Behavioral\State\States
       */
      class FillState extends AbstractState
      {
          const STATE = 'Fill';
      
          /**
           * @throws Exception
           */
          public function onFilled(): FillState
          {
              throw new Exception('Can\'t to fill filled glass.');
          }
      
          /**
           * @return EmptyState
           *
           * @throws Exception
           */
          public function onEmpty(): EmptyState
          {
              if (static::STATE === $this->getGlassState()) {
                  return new EmptyState($this->glass);
              }
      
              throw new Exception('Сan\'t pour the empty glass.');
          }
      }
      
                    
    • StateInterface.php
                    
      <?php
      
      namespace DesignPatterns\Behavioral\State\States;
      
      use DesignPatterns\Behavioral\State\Glass;
      
      /**
       * Interface StateInterface
       * @package DesignPatterns\Behavioral\State\States
       */
      interface StateInterface
      {
          /**
           * StateInterface constructor.
           *
           * @param Glass $glass
           */
          public function __construct(Glass $glass);
      
          /**
           * @return FillState
           */
          public function onFilled(): FillState;
      
          /**
           * @return EmptyState
           */
          public function onEmpty(): EmptyState;
      }
      
                    
  • Glass.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\State;
    
    use DesignPatterns\Behavioral\State\States\EmptyState;
    
    class Glass
    {
        protected $state;
    
        public function __construct()
        {
            $this->state = new EmptyState($this);
        }
    
        public function fill()
        {
            $this->state = $this->state->onFilled();
        }
        public function pour()
        {
            $this->state = $this->state->onEmpty();
        }
    
        public function getState()
        {
            return $this->state;
        }
    }
    
                  
  • Test case:

    StateTest.php
      
    <?php
    
    namespace DesignPatterns\Tests\Behavioral\State;
    
    use DesignPatterns\Behavioral\State\Glass;
    use Exception;
    use PHPUnit_Framework_TestCase;
    
    class StateTest extends PHPUnit_Framework_TestCase
    {
        public function testTryGlassInit()
        {
            $glass = new Glass();
            $this->assertInstanceOf('DesignPatterns\Behavioral\State\States\EmptyState', $glass->getState());
    
            $someGlass = new Glass();
            $this->assertNotInstanceOf('DesignPatterns\Behavioral\State\States\FillState', $someGlass->getState());
        }
    
        public function testTryFillGlass()
        {
            $glass = new Glass();
            $glass->fill();
            $this->assertInstanceOf('DesignPatterns\Behavioral\State\States\FillState', $glass->getState());
    
            $glass->pour();
            $glass->fill();
            $this->assertInstanceOf('DesignPatterns\Behavioral\State\States\FillState', $glass->getState());
        }
    
        public function testTryPourGlass()
        {
            $glass = new Glass();
            $glass->fill();
            $glass->pour();
            $this->assertInstanceOf('DesignPatterns\Behavioral\State\States\EmptyState', $glass->getState());
    
            $glass->fill();
            $glass->pour();
            $this->assertInstanceOf('DesignPatterns\Behavioral\State\States\EmptyState', $glass->getState());
        }
    
        public function testExceptionTryFillFilledGlass()
        {
            $this->expectException(Exception::class);
            $glass = new Glass();
            $glass->fill();
            $glass->fill();
        }
    
        public function testExceptionTryPourTheEmptyGlass()
        {
            $this->expectException(Exception::class);
            $glass = new Glass();
            $glass->pour();
        }
    }