bridge Міст (англ. Bridge) — структурний шаблон проєктування, який використовується в проєктуванні програмного забезпечення, щоб “розділяти абстракцію та реалізацію так, щоб вони могли змінюватися незалежно”. Шаблон міст використовує інкапсуляцію, агрегування та може використовувати успадкування для того, щоб розділити відповідальність між класами.

Спорідненим шаблоном для Моста є шаблон Адаптер, який поєднує пов’язані частини системи та надає простий інтерфейс.

У нас є філії у штатах та Європі. Кожен із регіонів має різну валюту. І підрахунки прибутку теж здійснюються по-різному. Але наші CashBox завжди повинні бути CashBox. Це і є абстракція. Деякі методи нашої абстракції залежать від аргументу, який передається. Тип вказаного інтерфейсу CurrencyInterface. Інтерфейс і є реалізація. Нам не потрібно щоразу успадковувати батька і міняти щось. У нас є один тип, і ми знаємо, як із ним працювати. Як бачимо, цей шаблон ми вже зустрічали у прикладах.

  • Abstraction
    • AbstractCashBox.php
                    
      <?php
      
      namespace DesignPatterns\Structural\Bridge\Abstraction;
      
      use DesignPatterns\Structural\Bridge\Implementation\CurrencyInterface;
      
      /**
       * Class AbstractCashBox
       * @package DesignPatterns\Structural\Bridge\Abstraction
       */
      abstract class AbstractCashBox
      {
          /**
           * @var CurrencyInterface
           */
          protected $defaultCurrency;
      
          /**
           * @var CurrencyInterface[]|float
           */
          protected $cash = [];
      
          /**
           * AbstractCashBox constructor.
           *
           * @param CurrencyInterface $currency
           */
          public function __construct(CurrencyInterface $currency)
          {
              $this->defaultCurrency = $currency;
          }
      
          /**
           * @return CurrencyInterface
           */
          public function getDefaultCurrency(): CurrencyInterface
          {
              return $this->defaultCurrency;
          }
      
          /**
           * @return float
           */
          abstract public function getCashInCashBox(): float;
      
          /**
           * @param CurrencyInterface $currency
           *
           * @return void
           */
          abstract public function setCash(CurrencyInterface $currency);
      }
      
                    
    • EuropeCashBox.php
                    
      <?php
      
      namespace DesignPatterns\Structural\Bridge\Abstraction;
      
      use DesignPatterns\Structural\Bridge\Implementation\CurrencyInterface;
      
      /**
       * Class EuropeCashBox
       * @package DesignPatterns\Structural\Bridge\Abstraction
       */
      class EuropeCashBox extends AbstractCashBox
      {
          /**
           * @return float
           */
          public function getCashInCashBox(): float
          {
              return array_sum($this->cash);
          }
      
          /**
           * @param CurrencyInterface $currency
           *
           * @return void
           */
          public function setCash(CurrencyInterface $currency)
          {
              $defaultCurrencyRate = $this->defaultCurrency->getCurrencyRate();
      
              array_push($this->cash, ($currency->getSum() * $defaultCurrencyRate) / $currency->getCurrencyRate());
          }
      }
      
                    
    • UsaCashBox.php
                    
      <?php
      
      namespace DesignPatterns\Structural\Bridge\Abstraction;
      
      use DesignPatterns\Structural\Bridge\Implementation\CurrencyInterface;
      
      /**
       * Class UsaCashBox
       * @package DesignPatterns\Structural\Bridge\Abstraction
       */
      class UsaCashBox extends AbstractCashBox
      {
          /**
           * @return float
           */
          public function getCashInCashBox(): float
          {
              $result = 0.0;
      
              $defaultCurrencyRate = $this->defaultCurrency->getCurrencyRate();
      
              foreach ($this->cash as $cash) {
                  $result += ($cash->getSum() * $defaultCurrencyRate) / $cash->getCurrencyRate();
              }
      
              return $result;
          }
      
          /**
           * @param CurrencyInterface $currency
           *
           * @return void
           */
          public function setCash(CurrencyInterface $currency)
          {
              array_push($this->cash, $currency);
          }
      }
      
                    
  • Implementation
    • AbstractCurrency.php
                    
      <?php
      
      namespace DesignPatterns\Structural\Bridge\Implementation;
      
      /**
       * Class AbstractCurrency
       * @package DesignPatterns\Structural\Bridge\Implementation
       */
      abstract class AbstractCurrency implements CurrencyInterface
      {
          /**
           * @var string
           */
          protected $symbol;
      
          /**
           * @var float
           */
          protected $sumCash = 0.0;
      
          /**
           * AbstractCurrency constructor.
           *
           * @param float $sumCash
           */
          public function __construct(float $sumCash = null)
          {
              if ($sumCash) {
                  $this->sumCash = $sumCash;
              }
          }
      
          /**
           * @return string
           */
          public function getCurrencySymbol(): string
          {
              return $this->symbol;
          }
      
          /**
           * @return float
           */
          public function getCurrencyRate(): float
          {
              return static::CURRENCY_RATE;
          }
      
          /**
           * @return float
           */
          public function getSum(): float
          {
              return $this->sumCash;
          }
      }
      
                    
    • CurrencyInterface.php
                    
      <?php
      namespace DesignPatterns\Structural\Bridge\Implementation;
      
      /**
       * Interface CurrencyInterface
       * @package DesignPatterns\Structural\Bridge\Implementation
       */
      interface CurrencyInterface
      {
          /**
           * CurrencyInterface constructor.
           *
           * @param float $sumCash
           */
          public function __construct(float $sumCash);
      
          /**
           * @return string
           */
          public function getCurrencySymbol(): string;
      
          /**
           * @return float
           */
          public function getCurrencyRate(): float;
      
          /**
           * @return float
           */
          public function getSum(): float;
      }
      
                    
    • DollarCurrency.php
                    
      <?php
      
      namespace DesignPatterns\Structural\Bridge\Implementation;
      
      /**
       * Class DollarCurrency
       * @package DesignPatterns\Structural\Bridge\Implementation
       */
      class DollarCurrency extends AbstractCurrency
      {
          /**
           * @var string
           */
          protected $symbol = '$';
      
          /**
           * @var float
           */
          const CURRENCY_RATE = 2.0;
      }
      
                    
    • EuroCurrency.php
                    
      <?php
      
      namespace DesignPatterns\Structural\Bridge\Implementation;
      
      /**
       * Class EuroCurrency
       * @package DesignPatterns\Structural\Bridge\Implementation
       */
      class EuroCurrency extends AbstractCurrency
      {
          /**
           * @var string
           */
          protected $symbol = '€';
      
          /**
           * @var float
           */
          const CURRENCY_RATE = 1;
      }
      
                    

    Test case:

    BridgeTest.php
      
    <?php
    namespace DesignPatterns\Tests\Structural\Bridge;
    
    use DesignPatterns\Structural\Bridge\Abstraction\EuropeCashBox;
    use DesignPatterns\Structural\Bridge\Abstraction\UsaCashBox;
    use DesignPatterns\Structural\Bridge\Implementation\CurrencyInterface;
    use DesignPatterns\Structural\Bridge\Implementation\DollarCurrency;
    use DesignPatterns\Structural\Bridge\Implementation\EuroCurrency;
    use PHPUnit_Framework_TestCase;
    
    class BridgeTest extends PHPUnit_Framework_TestCase
    {
        public function testDollarCurrencyInstanceOfCurrencyInterface()
        {
            $dollar = new DollarCurrency();
            $this->assertInstanceOf('DesignPatterns\Structural\Bridge\Implementation\CurrencyInterface', $dollar);
    
            return $dollar;
        }
    
        /**
         * @depends testDollarCurrencyInstanceOfCurrencyInterface
         *
         * @param CurrencyInterface $dollar
         */
        public function testDollarGetSymbol(CurrencyInterface $dollar)
        {
            $this->assertEquals('$', $dollar->getCurrencySymbol());
        }
    
        /**
         * @depends testDollarCurrencyInstanceOfCurrencyInterface
         *
         * @param CurrencyInterface $dollar
         */
        public function testDollarGetRate(CurrencyInterface $dollar)
        {
            $this->assertEquals(DollarCurrency::CURRENCY_RATE, $dollar->getCurrencyRate());
        }
    
        public function testDollarGetSum()
        {
            $val = 234.24;
            $dollar = new DollarCurrency($val);
    
            $this->assertEquals($val, $dollar->getSum());
        }
    
        public function testEuroCurrencyInstanceOfCurrencyInterface()
        {
            $euro = new EuroCurrency();
            $this->assertInstanceOf('DesignPatterns\Structural\Bridge\Implementation\CurrencyInterface', $euro);
    
            return $euro;
        }
    
        /**
         * @depends testEuroCurrencyInstanceOfCurrencyInterface
         *
         * @param CurrencyInterface $euro
         */
        public function testEuroGetSymbol(CurrencyInterface $euro)
        {
            $this->assertEquals('€', $euro->getCurrencySymbol());
        }
    
        /**
         * @depends testEuroCurrencyInstanceOfCurrencyInterface
         *
         * @param CurrencyInterface $euro
         */
        public function testEuroGetRate(CurrencyInterface $euro)
        {
            $this->assertEquals(EuroCurrency::CURRENCY_RATE, $euro->getCurrencyRate());
        }
    
        public function testEuroGetSum()
        {
            $val = 2555.24;
            $dollar = new EuroCurrency($val);
    
            $this->assertEquals($val, $dollar->getSum());
        }
    
        public function testUsaCashBoxInstanceOfAbstractCashBox()
        {
            $dollar = new DollarCurrency();
            $usaCashBox = new UsaCashBox($dollar);
    
            $this->assertInstanceOf('DesignPatterns\Structural\Bridge\Abstraction\AbstractCashBox', $usaCashBox);
        }
    
        public function testEuroCashBoxInstanceOfAbstractCashBox()
        {
            $euro = new EuroCurrency();
            $euroCashBox = new EuropeCashBox($euro);
    
            $this->assertInstanceOf('DesignPatterns\Structural\Bridge\Abstraction\AbstractCashBox', $euroCashBox);
        }
    
        public function testUsaCashBoxGetCashInCashBox()
        {
            $dollar = new DollarCurrency();
    
            $fiveDollars = new DollarCurrency(5);
            $sixDollars = new DollarCurrency(6);
            $tenEuros = new EuroCurrency(10);
    
            $usaCashBox = new UsaCashBox($dollar);
    
            $usaCashBox->setCash($fiveDollars);
            $this->assertEquals($fiveDollars->getSum(), $usaCashBox->getCashInCashBox());
    
            $usaCashBox->setCash($sixDollars);
            $this->assertEquals(
                $fiveDollars->getSum() + $sixDollars->getSum(),
                $usaCashBox->getCashInCashBox()
            );
    
            $usaCashBox->setCash($tenEuros);
            $defaultCurrencyRate = $usaCashBox->getDefaultCurrency()->getCurrencyRate();
    
            $this->assertEquals(
                $fiveDollars->getSum()
                + $sixDollars->getSum()
                + (($tenEuros->getSum() * $defaultCurrencyRate) / $tenEuros->getCurrencyRate()),
                $usaCashBox->getCashInCashBox()
            );
        }
    }