Kcko
3/29/2020 - 8:31 AM

6. Prototype

<?php

/*
    Návrhový vzor, který vám pomůže zamezit přemnožení tříd, se nazývá Prototype
    (Prototyp).
    Návrhový vzor Prototype určuje způsoby vytváření objektů pomocí prototypu objektu,
    který se při tvorbě nové instance zkopíruje (naklonuje).
*/

/*
    Definice 
    Návrhový vzor Prototype určuje způsoby vytváření objektů pomocí prototypu objektu,
    který se při tvorbě nové instance zkopíruje (naklonuje).
    K dosažení tohoto cíle je nutné provést následující kroky:
    1. Implementovat „správce prototypů“, který všechny prototypy, jež jsou
    v sy sté mu dostupné, uloží a zpřístupní. Tento správce musí poskytovat způsob,
    jak uložit nové prototypy pod určitým označením a následně je pod
    tímto označením zpřístupnit.
    2. V případě potřeby ve třídách, z nichž se budou vytvářet prototypy, implementovat
    metodu __clone(). Toto je nutné jen v případě, kdy prototypy
    obsahují reference na další objekty, které je nutné při klonování také klonovat.
    Pokud se takovéto reference v objektu nenacházejí, potom není nutné
    metodu __clone() implementovat.
    3. Vytvořit a inicializovat všechny prototypy, jež lze v systému využít, a předat
    je správci prototypů.
*/

class Book implements Publication
{
    // ... atributy třídy

    protected $cd = false;
    protected $symbol = null;
    // ... metody třídy
    public function setCd($cd)
    {
        $this->cd = $cd;
    }
    public function hasCd()
    {
        return $this->cd;
    }
    public function setSymbol($symbol)
    {
        $this->symbol = $symbol;
    }
    public function getSymbol()
    {
        return $this->symbol;
    }
}


class SpecialEditionPublisher 
{
    protected $prototype = array();

    public function addSpecialEdition($edition, Publication $pr) 
    {
        $this->prototype[$edition] = $pr;
    }

    public function publishPublication($edition)
    {
        if (!isset($this->prototype[$edition])) {
            throw new UnknownSpecialEditionException(
                'No prototype for special edition "' . $edition . '" registered.'
            );
        }
        return clone $this->prototype[$edition]; // CLONE 
    }
    
}

// [1]
$bookAlaItalia = new Book('italská kuchyně', 280);
$bookAlaItalia->setSymbol('Itálie');
$bookAlaItalia->setCd(true);
// klon
$bookAlaItalia2 = clone $bookAlaItalia;

// [2]
$bookAlaItalia = new Book('italská kuchyně', 280);
$bookAlaItalia->setSymbol('Itálie');
$bookAlaItalia->setCd(true);

$journalCars = new Journal('auto-moto', 100);
$journalCars->setSymbol('Auto');
$journalCars->setCd(false);

$publisher = new SpecialEditionPublisher();
$publisher->addSpecialEdition('A La Italia', $bookAlaItalia);
$publisher->addSpecialEdition('Vozidla 20. století', $journalCars);

// Debug
$book1 = $publisher->publishPublication('A La Italia');
print 'Typ: ' . get_class($book1) . "\n";
print 'Kategorie: ' . $book1->getCategory() . "\n";
print 'Počet stran: ' . $book1->getPageCount() . "\n";
print 'Symbol: ' . $book1->getSymbol() . "\n";
print 'CD: ' . ($book1->hasCd() ? 'ano' : 'ne') . "\n";

$car1 = $publisher->publishPublication('Vozidla 20. století');
print 'Typ: ' . get_class($car1) . "\n";
print 'Kategorie: ' . $car1->getCategory() . "\n";
print 'Počet stran: ' . $car1->getPageCount() . "\n";
print 'Symbol: ' . $car1->getSymbol() . "\n";
print 'CD: ' . ($car1->hasCd() ? 'ano' : 'ne') . "\n";

/*
    Typ: cz\k1886\publications\Book
    Kategorie: italská kuchyně
    Počet stran: 280
    Symbol: Itálie
    CD: ano
    
    -----------------------------------

    Typ: cz\k1886\publications\Journal
    Kategorie: auto-moto
    Počet stran: 100
    Symbol: Auto
    CD: ne
*/


// Klonovani mimo properties
/*
    Návrhový vzor Prototype vyžaduje, aby všechny vaše třídy, z nichž se mají vytvářet
    prototypy, šlo klonovat. To se na prvý pohled může jevit jednodušší, než jaké
    je to ve skutečnosti. Obsahují-li vaše prototypy v atributech jen skalární hodnoty,
    pak stačí použít operátor clone. Jsou-li však v atributech těchto tříd uložené
    reference na další objekty, musíte implementovat metodu __clone(), která se
    postará o vytvoření jejich kopií.
*/

class CD
{
    protected $track = 1;
    
    public function getTrack()
    {
        return $this->track;
    }

    public function setTrack($track)
    {
        $this->track = $track;
    }
}


class Book implements Publication
{
    // ... atributy třídy
    protected $cd;

    // ... metody třídy
    public function getCd()
    {
        return $this->cd;
    }

    public function setCd(CD $cd)
    {
        $this->cd = $cd;
    }
}


$bookAlaItalia = new Book('italská kuchyně', 280);
$bookAlaItalia->setSymbol('Itálie');
$bookAlaItalia->setCd(new CD());

$publisher = new SpecialEditionPublisher();
$publisher->addSpecialEdition('A La Italia', $bookAlaItalia);
$book1 = $publisher->publishPublication('A La Italia');
$book2 = $publisher->publishPublication('A La Italia');

print 'Aktuální stopa na CD v $book1: '
. $book1->getCd()->getTrack() . "\n";
print 'Aktuální stopa na CD v $book2: '
. $book2->getCd()->getTrack() . "\n";

/*
Aktuální stopa na CD v $book1: 1
Aktuální stopa na CD v $book2: 1
*/

$book2->getCd()->setTrack(5);

print 'Aktuální stopa na CD v $book1: '
. $book1->getCd()->getTrack() . "\n";
print 'Aktuální stopa na CD v $book2: '
. $book2->getCd()->getTrack() . "\n";

/*
Aktuální stopa na CD v $book1: 5
Aktuální stopa na CD v $book2: 5
*/

class Book implements Publication {
    // ... atributy a metody třídy
    public function __clone() 
    {
        // Thats the trick!
        $cd = clone $this->getCd();
        $this->setCd($cd);
    }
}