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