Kcko
4/1/2020 - 7:42 AM

9. Bridge

<?php

include "1.php";

/*
    Návrhový vzor Bridge odděluje rozhraní třídy od její vlastní implementace a umožňuje
    provádět jejich změnu nezávisle na sobě.
*/


/*
    Definice
    --------
    Návrhový vzor Bridge odděluje rozhraní třídy od její vlastní implementace a umožňuje
    provádět jejich změnu nezávisle na sobě.
    Pro dosažení tohoto cíle je nutné provést následující kroky:
    1. Definovat rozhraní nezbytné pro jednotlivé implementace.
    2. Vytvořit minimálně dvě třídy implementující rozhraní z bodu 1.
    3. Vytvořit způsob, jak abstrakci předat konkrétní implementaci.
    4. V abstrakci využívat služby implementace z bodu 2 přes rozhraní definované
    v bodě 1.


  Shrnutí
  -------
  Návrhový vzor Bridge odděluje abstrakci od její konkrétní implementace a zamezuje
  tak zbytečnému nárůstu počtu tříd při předávání implementací. Tento
  návrhový vzor řeší podobný problém jako návrhový vzor Adapter, představený
  v předchozí kapitole. Rozdíl mezi těmito dvěma vzory je především v tom, že
  vzor Bridge se používá již při návrhu aplikace, zatímco vzor Adapter řeší problém,
  jak zabezpečit komunikaci již hotových tříd.
*/

interface Storage 
{
    public function insertPublication($id, Publication $p);
    public function fetchPublications();
}


class StorageArray implements Storage
{
    protected $publications;

    public function insertPublication($id, Publication $p)
    {
        $this->publications[$id] = $p;
    }

    public function fetchPublications()
    {
        return $this->publications;
    }
}

// Upravena trida library oproti 1 dilu
abstract class AbstractLibrary
{
    protected $rentalActions = array();
    protected $debugger;
    protected $storage;

    public function __construct(Debugger $debugger, Storage $storage)
    {
        $this->debugger = $debugger;
        $this->storage = $storage;
    }

    protected function debug($message)
    {
        $this->debugger->debug($message);
    }

    public function rentPublication(Publication $p, Member $m)
    {
        $publicationId = array_search($p, $this->getPublications());

        if (false === $publicationId) {
            throw new UnknownPublicationException();
        }

        if (!$this->isPublicationAvailable($p)) {
            throw new PublicationNotAvailableException();
        }

        $rentalAction = new RentalAction($p, $m);
        $this->rentalActions[] = $rentalAction;
        $this->debug(
            $m->getName() . ' si vypůjčil publikaci: ' . $p->getCategory()
        );

        return $rentalAction;
    }

    public function returnPublication(Publication $p)
    {
        foreach ($this->rentalActions as $rentalAction) 
        {
            if ($rentalAction->getPublication() !== $p) {
                continue;
            }
            if ($rentalAction->isReturned()) {
                continue;
            }

            $rentalAction->markPublicationReturned();
            $this->debug(
                $rentalAction->getMember()->getName() .
                    ' vrátil publikaci: ' .
                    $p->getCategory()
            );

            return true;
        }

        return false;
    }

    public function isPublicationAvailable(Publication $p)
    {
        foreach ($this->rentalActions as $rentalAction) 
        {
            if ($rentalAction->getPublication() !== $p) {
                continue;
            }

            if ($rentalAction->isReturned()) {
                continue;
            }

            return false;
        }

        return true;
    }

    public function generateId()
    {
        $publications = $this->getPublications();
        return 'PUBLICATION-' . count($publications);
    }

    public function getPublications()
    {
        return $this->storage->fetchPublications();
    }

    abstract public function addToLibrary($id, Publication $p);
}


class Library extends AbstractLibrary
{
    public function addToLibrary($id, Publication $p)
    {
        $this->storage->insertPublication($id, $p);
        $this->debug('Nová publikace v knihovně: ' . $p->getCategory());
    }
}


// Storages
class StorageDatabase implements Storage
{
    protected $db;
    public function __construct($filename)
    {
        $this->db = new \SQLiteDatabase($filename, 0666);
        $this->initialiseDatabase();
    }
    public function insertPublication($id, Publication $p)
    {
        // INSERT TO DB
    }
    public function fetchPublications()
    {
       // SELECT FROM DB && FETCH to $publications (array or ...)
       return $publications
    }
    
    protected function initialiseDatabase() 
    {
     // connection to db
    }
}



class StorageArray implements Storage
{
    protected $publications;

    public function insertPublication($id, Publication $p)
    {
        $this->publications[$id] = $p;
    }

    public function fetchPublications()
    {
        return $this->publications;
    }
}


// USAGE

$publication1 = new Book('PC', 450);
$publication2 = new Book('MEDICINA', 50);

$storage = new StorageDatabase('library.sqlite');
$debugger = DebuggerEcho::getInstance();

$library = new Library($debugger, $storage);
$library->addToLibrary($library->generateId(), $publication1);
$library->addToLibrary($library->generateId(), $publication2);
$publications = $library->getPublications();

print_r($publications);