Kolejnym wzorcem projektowym, który omówię jest wzorzec Pośrednik (Proxy). Jest to wzorzec z grupy wzorców behawioralnych. Proxy jest wzorcem, który zastępuje obiekt o tym samym interfejsie i przed otrzymaniem dostępu do obiektu wykonuje się jakaś logika. Do czego może być użyty?

  • Najpopularniejszym użyciem proxy jest ograniczenie dostępu do kosztownych obiektów do momentu kiedy one są potrzebne. Jest to dość dobrze opisane w Internecie więc pominę to użycie.
  • Innym przykładem użycia proxy jest zabezpieczenie obiektu przed nieautoryzowanym dostępem.
  • Kolejna sytuacja kiedy możemy użyć pośrednika to inna reprezentacja obiektu w zależności od określonych warunków, wyobraź sobie, że masz obiekt ProxyRepository i w zależności od pewnych warunków chcesz, aby dane były pobierane albo z MySQL, albo MongoDB. Proxy to umożliwia. ’

Podsumowując Proxy przekierowuje żądania do oryginalnego obiektu wtedy, kiedy powinny być przekierowane, a warunki zależą od typu Proxy.

Proxy w praktyce

Wzorzec proxy

Wzorzec proxy

Dobra dość teorii, zobacz na kod. Jako przykład wybrałem drugi rodzaj pośrednika, który chroni obiekt przed nieautoryzowanym dostępem. Wyobraź sobie, że posiadasz obiekt samochodu i ten samochód by jeździć(to nie jest tesla) potrzebuje kierowcy. Jednakże, aby kierowca mógł jeździć musi być trzeźwy. W programie posiadamy, metodę, która będzie jeździła samochodami i nie interesuje jej sprawdzanie czy kierowca jest trzeźwy, załatwi to Pośrednik.

Do stworzenia wzorca będę potrzebował kilku plików:

CarInterface – interfejs do samochodu posiadający metodę drive(Driver $driver), która po dostaniu obiektu Driver powinna pojechać samochodem

<?php
namespace Rpodwika\Designpatterns\Behavioral\Proxy;
/**
 * Interface CarInterface
 * @package Rpodwika\Designpatterns\Behavioral\Proxy
 */
interface CarInterface
{
    /**
     * Method drives a car
     *
     * @param Driver $driver
     *
     * @return string
     */
    public function drive(Driver $driver);
}

Car – klasa implementująca interfejs samochodu, reprezentująca zachowanie pojazdu.

<?php
namespace Rpodwika\Designpatterns\Behavioral\Proxy;
/**
 * Class Car
 * @package Rpodwika\Designpatterns\Behavioral\Proxy
 */
class Car implements CarInterface
{
    /**
     * @inheritdoc
     */
    public function drive(Driver $driver)
    {
        return "I am driving a car";
    }
}

Driver – klasa reprezentująca kierowcę, lepiej byłoby stworzyć interfejs, ale by nie tworzyć wielu plików i by nie komplikować wzorca tym razem odbędzie się bez niego.  Klasa zawiera pola reprezentujące kierowcę i właściwość $isDrunk, która mówi o tym, czy kierowca jest trzeźwy.

&lt;?php
namespace Rpodwika\Designpatterns\Behavioral\Proxy;
/**
 * Class Driver
 * @package Rpodwika\Designpatterns\Behavioral\Proxy
 */
class Driver
{
    /**
     * @var string
     */
    private $name;
    /**
     * @var boolean
     */
    private $isDrunk;
    /**
     * @param $name
     * @param $isDrunk
     */
    public function __construct($name, $isDrunk)
    {
        $this-&gt;name = $name;
        $this-&gt;isDrunk = $isDrunk;
    }
    /**
     * @return string
     */
    public function getName()
    {
        return $this-&gt;name;
    }
    /**
     * @param string $name
     *
     * @return $this
     */
    public function setName($name)
    {
        $this-&gt;name = $name;
        return $this;
    }
    /**
     * @return boolean
     */
    public function isDrunk()
    {
        return $this-&gt;isDrunk;
    }
    /**
     * @param boolean $isDrunk
     *
     * @return $this
     */
    public function setIsDrunk($isDrunk)
    {
        $this-&gt;isDrunk = $isDrunk;
        return $this;
    }
}

Ostatnim niezbędnym plikiem jest ProxyCar który będzie reprezentował samochód, do którego, aby kierować, potrzebne jest bycie trzeźwym.

<?php
namespace Rpodwika\Designpatterns\Behavioral\Proxy;
/**
 * Class ProxyCar
 * @package Rpodwika\Designpatterns\Behavioral\Proxy
 */
class ProxyCar implements CarInterface
{
    /**
     * @var CarInterface
     */
    private $car;
    public function __construct()
    {
        $this->car = new Car();
    }
    /**
     * @inheritdoc
     */
    public function drive(Driver $driver)
    {
        if ($driver->isDrunk()) {
            return 'Driver cannot drive';
        }

        return $this->car->drive($driver);
    }
}

Klasa ta implementuje interfejs samochodu, ale w metodzie drive(Driver $driver) sprawdza przed oddaniem dostępu do oryginalnego obiektu samochodu, czy kierowca jest trzeźwy.
Teraz załóżmy, że mamy funkcje, która przyjmuje jako argument interfejs CarInterface.

$drunkDriver = new Driver('Driver a', true);
$soberDriver = new Driver('Driver b', false);
$car = new Car();
$smartCar = new ProxyCar();


function driveCar(CarInterface $car, Driver $driver)
{ 
   $car->drive($driver);
}

driveCar($car, $drunkDriver); // funkcja pozwoli jechać pijanemu kierowcy
driveCar($smartCar, $drunkDriver); // funkcja nie pozwoli na jazde pijanemu kierowcy
driveCar($car, $soberDriver); // funkcja pozwoli jechać
driveCar($smartCar, $soberDriver); // funkcja pozwoli jechać

 

Zauważ, że funkcja driveCar nie wie czy dostała ProxyCar czy Car i jej to nie interesuje, najważniejsze, że dostała klasę, która implementuje interfejs CarInterface, całe sprawdzenie jest w obiekcie pośrednika, który zachowuje się tak jak Car, ale w określonym przypadku nie pozwoli na dostęp do obiektu. I to właśnie jest cała esencja wzorca projektowego pośrednik.

Jeśli masz jakieś pytania, znalazłeś błąd lub masz sugestię co do artykułu pisz śmiało.

Cały kod dostępny jest w moim repozytorium na github gdzie znajdziesz implementacje innych wzorców projektowych.