<?php
include "1.php";
/*
Definice
--------
Návrhový vzor Decorator rozšiřuje daný objekt o novou funkčnost nebo změní stávající
metody za běhu programu. Tento návrhový vzor nabízí flexibilní alternativu
k vytváření potomků tříd.
Pro dosažení požadovaného výsledku jsou nutné následující kroky:
1. Vytvořit základní třídu pro dekorátory, která je stejného typu jako objekt,
který má být dekorovaný. Vytvořit potomka této třídy nebo v nové třídě
implementovat všechny metody rozhraní.
2. V základní třídě definovat konstruktor, jemuž bude možné předat dekorovaný
objekt. Tento objekt uložit ve třídě dekorátoru.
3. Implementovat všechny metody základní třídy takovým způsobem, že se
jejich volání bude delegovat na objekt, který se má dekorovat.
4. Implementovat libovolné konkrétní dekorátory, jež jsou potomky třídy
z bodu 3. Konkrétního chování dekorátoru se docílí přepsáním jednotlivých
metod.
5. Kombinací více dekorátorů lze dosáhnout jejich vzájemným dekorováním.
*/
abstract class PublicationDecorator implements Publication
{
protected $publication;
public function __construct(Publication $publication)
{
$this->publication = $publication;
}
public function open()
{
return $this->publication->open();
}
public function close()
{
return $this->publication->close();
}
public function setPageNumber($page)
{
return $this->publication->setPageNumber($page);
}
public function getPageNumber()
{
return $this->publication->getPageNumber();
}
public function getDailyRate($days = 1)
{
return $this->publication->getDailyRate($days);
}
public function getCategory()
{
return $this->publication->getCategory();
}
public function getPageCount()
{
return $this->publication->getPageCount();
}
public function __call($method, $args)
{
return call_user_func_array(
array($this->publication, $method), $args
);
}
public function providesMethod($method)
{
if (method_exists($this->publication, $method))
{
return TRUE;
}
if ($this->publication instanceof PublicationDecorator)
{
return $this->publication->providesMethod($method);
}
return FALSE;
}
public function getPublication()
{
return $this->publication;
}
}
class PublicationDecoratorAppendix extends PublicationDecorator
{
public function getDailyRate($days = 1)
{
$rate = $this->publication->getDailyRate($days);
return $rate + 2;
}
public function getPageCount()
{
$pageCount = $this->publication->getPageCount();
return $pageCount + 30;
}
}
// Použití
$book = new Book('medicína', 100);
$withAppendix = new PublicationDecoratorAppendix($book);
printf("Celkový počet stran s přílohou: %d\n",
$withAppendix->getPageCount()
);
printf("Denní sazba: %.2f Kč\n",
$withAppendix->getDailyRate()
);
/*
Celkový počet stran s přílohou: 130
Denní sazba: 12.00 Kč
*/
// Další decorator
class PublicationDecoratorNews extends PublicationDecorator
{
public function getDailyRate($days = 1)
{
$rate = $this->publication->getDailyRate($days);
return round($rate * 0.8, 2);
}
}
$newsWithAppendix = new PublicationDecoratorNews($withAppendix);
printf("Celkový počet stran s přílohou: %d\n",
$newsWithAppendix->getPageCount()
);
printf("Denní sazba: %.2f Kč\n",
$newsWithAppendix->getDailyRate()
);
/*
Celkový počet stran s přílohou: 130
Denní sazba: 9.60 Kč
*/
/*
Další využití
-------------
Lze doplnit i nové metody
*/
class PublicationDecoratorCd extends PublicationDecorator
{
protected $track = 1;
public function getTrack()
{
return $this->track;
}
public function setTrack($track)
{
$this->track = $track;
}
public function getDailyRate($days = 1)
{
$rate = $this->publication->getDailyRate($days);
return $rate * 1.3;
}
}
$book = new Book('italská kuchyně', 280);
$withCd = new PublicationDecoratorCd($book);
printf("Denní sazba: %.2f Kč\n", $withCd->getDailyRate());
$withCd->setTrack(10);
printf("Aktuální stopa: %d \n", $withCd->getTrack());
/*
Denní sazba: 13.00 Kč
Aktuální stopa: 10
*/
/*
Může vzniknout problém, že jiný dekorátor tyto metody nevidí,
odchytne se to pres __call, ale je to takové magické...
*/
$book = new Book('italská kuchyně', 280);
$withCd = new PublicationDecoratorCd($book);
$newsWithCd = new PublicationDecoratorNews($withCd);
printf("Denní sazba: %.2f Kč\n", $newsWithCd->getDailyRate());
$newsWithCd->setTrack(10);
// printf("Aktuální stopa: %d \n", $newsWithCd->getTrack());
/*
Denní sazba: 10.40 Kč
Aktuální stopa: 10
*/
/*
Dá se to vyřešit lépe...
viz vlastní metoda na existenci providesMethods()
*/
$book = new Book('italská kuchyně', 280);
$withCd = new PublicationDecoratorCd($book);
$newsWithCd = new PublicationDecoratorNews($withCd);
if ($newsWithCd->providesMethod('setTrack'))
{
var_dump($newsWithCd->getPublication());
$newsWithCd->setTrack(30);
}
printf("Aktuální stopa: %d \n", $newsWithCd->getTrack());