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();
        }
    }