<?php
include "1.php";
/*
Návrhový vzor Adapter přizpůsobí rozhraní určité třídy rozhraní požadovanému
klientem a umožní tak spolupráci tříd, které by kvůli nekompatibilním rozhraním
nebyly schopné spolupracovat.
Definice
---------
Návrhový vzor Adapter přizpůsobí rozhraní určité třídy rozhraní požadovanému
klientem a umožní tak spolupráci tříd, které by kvůli nekompatibilním rozhraním
nebyly schopné spolupracovat.
Pro vytvoření adaptéru, který umožní spolupráci dvou nekompatibilních rozhraní,
je nutné provést následující kroky:
1. Lokalizovat rozdíly mezi nabízeným a požadovaným rozhraním.
2. Implementovat novou třídu, která bude poskytovat požadované rozhraní.
3. Vytvořit způsob, jak předat adaptéru objekt, který má být přizpůsobený
– například pomocí principu vkládání závislostí (Dependency Injection).
4. Implementovat všechny metody vyžadované rozhraním a delegovat požadavky
dále na příslušné metody původního objektu.
5. Přihlížet k signalizování chyb.
6. V aplikaci využívat objekt adaptéru a pomocí něho „obalit“ původní objekt.
*/
class Title
{
const INFO_CATEGORY = 'category';
const INFO_PAGECOUNT = 'pageCount';
const TITLE_OPEN = 1;
const TITLE_CLOSE = 0;
protected $info = array();
protected $currentPage = 0;
protected $state = self::TITLE_CLOSE;
public function __construct($pageCount, $category)
{
$this->info[self::INFO_PAGECOUNT] = $pageCount;
$this->info[self::INFO_CATEGORY] = $category;
}
public function getInfo($info)
{
if (isset($this->info[$info])) {
return $this->info[$info];
}
}
public function setClosed($state)
{
$this->state = $state;
}
public function getClosed()
{
return $this->state;
}
public function setCurrentPage($page)
{
if (self::TITLE_CLOSE == $this->state) {
$this->setClosed(self::TITLE_OPEN);
}
$this->currentPage = $page;
}
public function getCurrentPage()
{
if (self::TITLE_CLOSE == $this->state) {
throw new ClosedException('Kniha není otevřena');
}
return $this->currentPage;
}
}
// Lisi se nazvy metod, parametry atd ...
$title = new Title(100, 'PC');
$title->setCurrentPage(47);
printf(
"Kategorie: %s\n", $title->getInfo(Title::INFO_CATEGORY)
);
printf(
"Počet stran: %d\n", $title->getInfo(Title::INFO_PAGECOUNT)
);
printf(
"Kniha je otevřena na %d. straně\n", $title->getCurrentPage()
);
$book = new Book('PC', 100);
$book->open();
$book->setPageNumber(47);
printf(
"Kategorie: %s\n", $book->getCategory()
);
printf(
"Počet stran: %d\n", $book->getPageCount()
);
printf(
"Kniha je otevřena na %d. straně\n", $book->getPageNumber()
);
// OUTPUT
/*
Kategorie: PC
Počet stran: 100
Kniha je otevřena na 47. straně
Kategorie: PC
Počet stran: 100
Kniha je otevřena na 47. straně
*/
<?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);
<?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());
<?php
include "1.php";
/*
Definice
--------
Návrhový vzor Proxy kontroluje přístup k objektu pomocí zástupce, který se používá
místo vlastního objektu.
Pro implementaci zástupce typu Protection Proxy je nutné provést následující
kroky:
1. Vytvořit třídu, která implementuje veškerá rozhraní implementované třídou,
jejíž instance mají být chráněné.
2. Ve všech metodách delegovat volání na objekt, který provede požadovanou
úlohu.
3. Vytvořit potomka třídy z bodu 2 a v relevantních metodách implementovat
kontrolní mechanizmy.
*/
abstract class PublicationProxy 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();
}
}
class PrepaidPublicationProxy extends PublicationProxy
{
protected $startPage;
protected $endPage;
public function __construct(Publication $publication, $startPage, $endPage)
{
parent::__construct($publication);
$this->startPage = $startPage;
$this->endPage = $endPage;
}
public function setPageNumber($page)
{
if ($this->startPage > $page || $this->endPage < $page) {
throw new OutOfRangeException(
$page,
$this->startPage,
$this->endPage
);
}
return $this->publication->setPageNumber($page);
}
}
class OutOfRangeException extends \Exception
{
protected $page;
protected $startPage;
protected $endPage;
public function __construct($page, $startPage, $endPage)
{
$this->page = $page;
$this->startPage = $startPage;
$this->endPage = $endPage;
$this->message = sprintf(
"Požadovaná strana číslo %dse nenachází v předplaceném rozsahu stran %d - %d.",
$this->page,
$this->startPage,
$this->endPage
);
}
public function getPage()
{
return $this->page;
}
public function getStartPage()
{
return $this->startPage;
}
public function getEndPage()
{
return $this->endPage;
}
}
$book = new Book('medicína', 650);
$proxy = new PrepaidPublicationProxy($book, 261, 414);
$proxy->open();
try
{
$proxy->setPageNumber(300);
print "Kniha je otevřená na straně 300.\n";
$proxy->setPageNumber(115);
print "Kniha je otevřená na straně 115.\n";
}
catch (OutOfRangeException $e) {
print $e->getMessage() . "\n";
}
$proxy->close();
/*
Kniha je otevřená na straně 300.
Požadovaná strana číslo 115 se nenachází
v předplaceném rozsahu stran 261-414.
*/
<?php
/*
Návrhový vzor Facade poskytuje zjednodušené rozhraní pro sérii rozhraní. Nabízí
rozhraní, které zjednodušuje používání základního systému.
Pro implementaci návrhového vzoru Facade jsou nutné následující kroky:
1. Identifikovat třídy a objekty systému, které mají být schované za fasádou.
2. Definovat operace, které má fasáda umožnit.
3. Implementovat třídy fasády a vytvořit metody, pomocí nichž je fasáda
schopná přistupovat ke schovaným komponentám.
4. Ve třídě fasády implementovat metody, které umožní zjednodušený přístup
k jednotlivým komponentám systému za účelem provedení různých operací.
Na první pohled se návrhový vzor Facade podobá vzoru Adapter. Avšak fasáda
se na rozdíl od adaptéru nevyužívá k přizpůsobení jednoho rozhraní druhému.
Fasáda zjednodušuje stávající rozhraní, redukuje počet tříd, které musí klient
používat, a zjednodušuje použitelnost určitého subsystému.
Fasáda rovněž prosazuje něco,
*/
/*
Příklad v knize je takový nic moc.
*/
class FacadePurchase
{
protected $library = null;
protected $publishers = array();
public function __construct(Library $library)
{
$this->library = $library;
}
public function addPublisher($id, AbstractPublisher $publisher)
{
$this->publishers[$id] = $publisher;
}
public function purchase($publisher, $pageCount)
{
if (!isset($this->publishers[$publisher])) {
throw new UnknownPublisherException('Neznámý vydavatel');
}
$publication = $this->publishers[$publisher]->sellPublication(
$pageCount
);
$id = IdGenerator::getInstance()->generateId();
$this->library->addToLibrary($id, $publication);
return $publication;
}
}
// USAGE
$debugger = DebuggerEcho::getInstance();
$library = new Library($debugger);
$bookPublisher = new BookPublisher('PC');
$journalPublisher = new JournalPublisher('medicína');
$facade = new FacadePurchase($library);
$facade->addPublisher('PC', $bookPublisher);
$facade->addPublisher('MEDICINA', $journalPublisher);
$facade->purchase('PC', 460);
$facade->purchase('MEDICINA', 300);
<?php
/*
Návrhový vzor Flyweight zabezpečuje společné využívání velmi podobných objektů,
aby je bylo možné použít ve velkém počtu bez nárůstu spotřeby paměti.
Při implementaci návrhového vzoru Flyweight to neznamená, že příslušné třídy
nesmějí obsahovat žádný atribut, který by uchovával jejich stav. Při implementaci
se rozlišuje mezi vnitřním (nebo též vlastní – angl. intrinsic) a vnějším (nebo též
nevlastní – angl. extrinsic) stavem. Vnitřní stav návrhový vzor i nadále uchovává
a obsahuje výlučně informace, které jsou nezávislé na obsahu. Vnější stav je závislý
na obsahu, a proto jej nelze uchovávat a společně využívat ve třídách návrhového
vzoru Flyweight.
Definice
----------
Návrhový vzor Flyweight zabezpečuje společné využívání velmi podobných objektů,
aby je bylo možné použít ve velkém počtu bez nárůstu spotřeby paměti.
Pro implementaci návrhového vzoru Flyweight jsou nutné následující kroky:
1. Identifikovat třídu, z níž se vytváří velké množství instancí.
2. Tuto třídu osvobodit od co největšího počtu objektů, které uchovávají stav
konkrétní implementace. Tento stav instancí uložit někde mimo instance.
3. Implementovat třídu, která slouží jako zástupce „mušího“ objektu.
*/
// optimalizace pro Muší vahu :), kod z Factory kap. 3
// OLD
abstract class Cell
{
protected $content = null;
public function __construct($content)
{
$this->content = $content;
}
abstract public function show();
}
// NEW
abstract class Cell
{
abstract public function show($data);
}
// OLD
class TextCell extends Cell
{
public function show()
{
$diff = strlen($this->content) - mb_strlen($this->content, 'UTF-8');
print '|' . str_pad($this->content, 15 + $diff);
}
}
// NEW
class TextCell extends Cell
{
public function show($data)
{
$diff = strlen($data) - mb_strlen($data, 'UTF-8');
print '|' . str_pad($data, 15 + $diff);
}
}
// OLD
interface TableFactory
{
public function createCell($content);
public function createRow();
public function createHeader();
public function createTable();
}
// NEW
interface TableFactory
{
public function createCell();
public function createRow();
public function createHeader();
public function createTable();
}
// OLD
abstract class Row
{
protected $cellData = array();
public function addCell($cell)
{
$this->cellData[] = $cell;
}
abstract public function show();
}
// NEW
abstract class Row
{
protected $cellData = array();
protected $flyWeightCell;
public function __construct(Cell $cell)
{
$this->flyWeightCell = $cell;
}
// ... ostatní metody třídy
}
// OLD
class TextRow extends Row
{
public function show()
{
foreach ($this->cells as $cell) {
$cell->show();
}
print "|\n";
$str = '';
for ($i = 0; $i < count($this->cells); $i++) {
$str .= '+' . str_repeat('-', 15);
}
print $str . "+\n";
}
}
// NEW
class TextRow extends Row
{
public function show()
{
foreach ($this->cellData as $data) {
$this->flyWeightCell->show($data);
}
print "|\n";
$str = '';
for ($i = 0; $i < count($this->cellData); $i++) {
$str .= '+' . str_repeat('-', 15);
}
print $str . "+\n";
}
}
// OLD
class TextHeader extends Header
{
public function show()
{
$str = '';
for ($i = 0; $i < count($this->cells); $i++)
{
$str .= '+' . str_repeat('-', 15);
}
print $str . "+\n";
foreach ($this->cells as $cell)
{
$cell->show();
}
print "|\n";
print $str . "+\n";
}
}
// NEW
class TextHeader extends Header
{
public function show()
{
$str = '';
for ($i = 0; $i < count($this->cellData); $i++)
{
$str .= '+' . str_repeat('-',15);
}
print $str . "+\n";
foreach ($this->cellData as $data)
{
$this->flyWeightCell->show($data);
}
print "|\n";
print $str . "+\n";
}
}
// OLD
class TextTableFactory implements TableFactory
{
public function createCell($content)
{
return new TextCell($content);
}
public function createRow()
{
return new TextRow();
}
public function createHeader()
{
return new TextHeader();
}
public function createTable()
{
return new TextTable();
}
}
// NEW
class TextTableFactory implements TableFactory
{
private $cell = null;
public function createCell()
{
if (null === $this->cell) {
$this->cell = new TextCell();
}
return $this->cell;
}
public function createRow()
{
$row = new TextRow($this->createCell());
return $row;
}
public function createHeader()
{
$header = new TextHeader($this->createCell());
return $header;
}
public function createTable()
{
return new TextTable();
}
}
// OLD
class PublicationList
{
protected $tableFactory = null;
public function __construct(TableFactory $tf)
{
$this->tableFactory = $tf;
}
public function displayTable($data)
{
$table = $this->tableFactory->createTable();
$header = $this->tableFactory->createHeader();
$header->addCell($this->tableFactory->createCell('Kategorie'));
$header->addCell($this->tableFactory->createCell('Počet stran'));
$table->setHeader($header);
foreach ($data as $line) {
$row = $this->tableFactory->createRow();
foreach ($line as $field) {
$cell = $this->tableFactory->createCell($field);
$row->addCell($cell);
}
$table->addRow($row);
}
$table->show();
}
}
// NEW
class PublicationList
{
protected $tableFactory = null;
public function __construct(TableFactory $tf)
{
$this->tableFactory = $tf;
}
public function displayTable($data)
{
$table = $this->tableFactory->createTable();
$header = $this->tableFactory->createHeader();
$header->addCell('Kategorie');
$header->addCell('Počet stran');
$table->setHeader($header);
foreach ($data as $line) {
$row = $this->tableFactory->createRow();
$table->addRow($row);
foreach ($line as $field) {
$row->addCell($field);
}
}
$table->show();
}
}
// USAGE, stale stejne jen se zmenil pocet objektů
/*
Tam, kde se v původní implementaci používaly rozdílné instance třídy cz\k1886\
tables\text\TextCell, jsou nyní všude odkazy na stejnou instanci.
Tuto skutečnost můžete vidět na čísle za instancí. V tomto případě je to #5. Místo osmi
instancí této třídy tedy potřebujete už jen jednu, čímž jste zredukovali počet
potřebných instancí o 87,5 %.
Je samozřejmé, že můžete v redukování objektů pokračovat i dále a použít muší
váhu i na objekty řádků tabulky
*/
$publications = array(
array('PC', 100),
array('PC', 380),
array('medicína', 250),
array('historie', 70),
);
$pl = new PublicationList(new TextTableFactory());
$pl->displayTable($publications);
<?php
/*
Definice
---------
Návrhový vzor Observer definuje závislost 1:n mezi subjektem a libovolným počtem
pozorovatelů. Při změně stavu subjektu jsou o této skutečnosti automaticky informovány
všechny pozorovatele.
Pro implementaci tohoto návrhového vzoru jsou nutné následující kroky:
1. Definovat rozhraní (Observer) pro pozorovatele, které vyžaduje jednu
metodu, prostřednictvím níž jsou pozorovatelé informovaní o změně stavu
objektu.
2. Definovat rozhraní (Observable) pro pozorované objekty. Toto rozhraní
musí vyžadovat metody na přidání a odstranění pozorovatelů a také metodu
notify(), pomocí níž jsou pozorovatele informovány o změně stavu
pozorovaného objektu. rozdílem. Pozorovatel žádné hodnoty týkající se pozorovaného objektu – je bezstavový. Tento pozorovatel
tedy může současně pozorovat více objektů. Pozorovatel naopak uchovává informaci, kdy má nastat další pravidelná kontrola – uchovává
určitý stav. Protože se tato hodnota může u různých tiskáren lišit, lze jej použít
jen pro pozorování jednoho objektu. Pro každý další objekt je nutné vytvořit nový objekt pozorovatele.
3. V pozorovaném objektu implementovat rozhraní Observable.
4. Do metod, které mění stav objektu, vložit volání metody notify().
5. Implementovat konkrétní pozorovatele.
*/
interface Printer
{
public function turnOn();
public function turnOff();
public function printPublication(Publication $p, $count = 1);
public function getType();
public function getPageCounter();
}
interface Observable
{
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}
interface Observer
{
public function update(Observable $observable);
}
class LaserPrinter implements Printer, Observable
{
protected $turnedOn = false;
protected $type = null;
protected $pageCounter = 0;
protected $observers = array();
function __construct($type)
{
$this->type = $type;
}
public function turnOn()
{
$this->turnedOn = true;
}
public function turnOff()
{
$this->turnedOff = false;
}
public function getType()
{
return $this->type;
}
public function printPublication(Publication $p, $count = 1)
{
if (true !== $this->turnedOn) {
return false;
}
$pageCount = $p->getPageCount();
$this->pageCounter += $pageCount * $count;
// 2 OBSERVER
$this->notify();
return true;
}
public function getPageCounter()
{
return $this->pageCounter;
}
// OBSERVE THINGS
public function attach(Observer $observer)
{
$this->observers[] = $observer;
}
public function detach(Observer $observer)
{
$this->observers = array_diff($this->observers, array($observer));
}
public function notify()
{
foreach($this->observers as $observer)
{
$observer->update($this);
}
}
}
class ObserverInitialCheck implements Observer
{
protected $checkPageCounter;
public function __construct($pageCounter = 10000)
{
$this->checkPageCounter = $pageCounter;
}
public function update(Observable $observable)
{
if (!$observable instanceof Printer) {
return;
}
if ($observable->getPageCounter() >= $this->checkPageCounter) {
printf(
"Nutná prvotní kontrola po %d vytištěných stranách.\n",
$this->checkPageCounter
);
$observable->detach($this);
}
}
}
$book = new Book('PC', 580);
$printer = new LaserPrinter('Printer 1');
$initialCheck = new ObserverInitialCheck(10000);
$printer->attach($initialCheck);
$printer->turnOn();
$printer->printPublication($book, 10);
printf("Počet vytištěných stran: %d\n",
$printer->getPageCounter());
$printer->printPublication($book, 10);
printf("Počet vytištěných stran: %d\n",
$printer->getPageCounter());
$printer->turnOff();
/*
Počet vytištěných stran: 5800
Nutná prvotní kontrola po 10000 vytištěných stranách.
Počet vytištěných stran: 11600
*/
class ObserverRegularCheck implements Observer
{
protected $nextCheck = null;
protected $interval;
public function __construct($start, $interval)
{
$this->nextCheck = $start;
$this->interval = $interval;
}
public function update(Observable $observable)
{
if (!$observable instanceof Printer) {
return;
}
if ($observable->getPageCounter() >= $this->nextCheck)
{
printf(
"Pravidelná kontrola po %d vytištěných stranách.\n",
$this->nextCheck
);
$this->nextCheck += $this->interval;
}
}
}
$book = new Book('PC', 580);
$printer = new LaserPrinter('Printer 1');
$initialCheck = new ObserverInitialCheck(10000);
$regularCheck = new ObserverRegularCheck(40000, 30000);
$printer->attach($initialCheck);
$printer->attach($regularCheck);
$printer->turnOn();
$printer->printPublication($book, 20);
printf("Počet vytištěných stran: %d\n",
$printer->getPageCounter());
$printer->printPublication($book, 60);
printf("Počet vytištěných stran: %d\n",
$printer->getPageCounter());
$printer->printPublication($book, 50);
printf("Počet vytištěných stran: %d\n",
$printer->getPageCounter());
$printer->turnOff();
/*
Nutná prvotní kontrola po 10000 vytištěných stranách.
Počet vytištěných stran: 11600
Pravidelná kontrola po 40000 vytištěných stranách.
Počet vytištěných stran: 46400
Pravidelná kontrola po 70000 vytištěných stranách.
Počet vytištěných stran: 75400
*/
<?php
/*
Definice
---------
Návrhový vzor Mediator odstraňuje vzájemné vazby mezi komunikujícími objekty
a tím zajišťuje možnost vzájemné komunikace mezi objekty, které nejsou v přímé
interakci. Zavedením návrhového vzoru Mediator se mění vazby mezi objekty
z typu m:n na 1:n.
Pro implementaci tohoto návrhového vzoru jsou nezbytné následující kroky:
1. Definovat rozhraní prostředníka.
2. Definovat rozhraní objektů, které budou ke komunikaci využívat objekt
definovaný v bodě 1. Toto rozhraní musí obsahovat metodu, kterou bude
volat konkrétní prostředník.
3. Implementovat konkrétního prostředníka a v něm požadovanou logiku
zabezpečující komunikaci mezi objekty. V případě, že by byla požadovaná
logika velmi komplexní, definovat pomocné třídy prostředníka.
4. Implementovat konkrétního uživatele.
Návrhový vzor Mediator je konkurenčním návrhovým vzorem
k vzoru Observer, probíranému v předchozí kapitole, avšak oproti němu nedisponuje
možnostmi dynamického přidávání a odebírání objektů zajímajících se
o změny.
*/
interface Mediator
{
public function send($message, AbstractEmployee $employee);
}
abstract class AbstractEmployee
{
protected $id;
protected $mediator;
public function __construct($id, Mediator $mediator)
{
$this->setId($id);
$this->setMediator($mediator);
}
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setMediator(Mediator $mediator)
{
$this->mediator = $mediator;
}
public function getMediator()
{
return $this->mediator;
}
public function send($message)
{
$this->mediator->send($message, $this);
}
abstract public function receive($message);
}
class NewsMediator implements Mediator
{
protected $employees = array();
public function registerEmployee(AbstractEmployee $employee)
{
$this->employees[] = $employee;
}
public function send($message, AbstractEmployee $sender)
{
foreach ($this->employees as $employee) {
if ($employee !== $sender) {
$employee->receive($message);
}
}
}
}
class Employee extends AbstractEmployee
{
public function receive($message)
{
printf("<%s> Přijatá zpráva: %s\n", $this->getId(), $message);
}
}
$mediator = new NewsMediator();
$employee1 = new Employee('EMP1', $mediator);
$employee2 = new Employee('EMP2', $mediator);
$employee3 = new Employee('EMP3', $mediator);
$employee4 = new Employee('EMP4', $mediator);
$mediator->registerEmployee($employee1);
$mediator->registerEmployee($employee2);
$mediator->registerEmployee($employee3);
$mediator->registerEmployee($employee4);
$employee3->send('Zpráva od EMP3');
$employee1->send('Odpověď od EMP1');
<?php
/*
Návrhový vzor Template Method definuje rozhraní algoritmu v metodě a přenechává
implementaci jednotlivých kroků potomkům třídy. Ti mohou jednotlivé části
algoritmu modifikovat, aniž by změnili jeho strukturu.
*/
abstract class AbstractPrinter implements Printer, Observable
{
// ... atributy a metody původní třídy LaserPrinter
protected $paperAmount = 150;
abstract protected function replaceCartridges();
abstract protected function checkPrintingParts();
abstract protected function isLowPaperAmount();
protected function addPaper()
{
printf(
"Doplnění %d listů do zásobníku papíru.\n",
1000 - $this->paperAmount
);
$this->paperAmount = 1000;
}
final public function check()
{
printf("Probíhá kontola tiskárny %s:\n", $this->type);
$this->replaceCartridges();
$this->checkPrintingParts();
if ($this->isLowPaperAmount()) {
$this->addPaper();
} else {
print "Dostatečné množství papíru.\n";
}
}
}
class LaserPrinter extends AbstractPrinter
{
protected function replaceCartridges()
{
print "Výměna toneru typu AAA-111.\n";
}
protected function checkPrintingParts()
{
print "Kontrola fotoválce.\n";
}
protected function isLowPaperAmount()
{
if ($this->paperAmount < 200) {
return true;
}
return false;
}
}
class InkjetPrinter extends AbstractPrinter
{
protected function replaceCartridges()
{
print "Výměna inkoustových náplní typu BBB-222.\n";
}
protected function checkPrintingParts()
{
print "Kontrola trysek.\n";
}
protected function isLowPaperAmount()
{
if ($this->paperAmount < 100) {
return true;
}
return false;
}
}
$laserPrinter = new LaserPrinter('LP-1');
$inkjetPrinter = new InkjetPrinter('IP-1');
$laserPrinter->check();
$inkjetPrinter->check();
<?php
/*
Definice
Návrhový vzor Command zapouzdřuje jednotlivé požadavky jako objekty. Díky
tomu lze parametrizovat jiné objekty s požadavky, přidávat požadavky do fronty
a podporovat operace, které je možné provádět zpětně.
Pro implementaci tohoto návrhového vzoru jsou nutné následující kroky:
1. Definovat rozhraní pro jednotlivé příkazy.
2. Vytvořit konkrétní příkazy, které implementují rozhraní z bodu 1 a ve třídě
zapouzdřují jednotlivé požadavky.
3. Vytvořit způsob, který umožní
*/
// BAD
interface PublishingProcess {
public function publish(Publication $publication);
}
class PrintedBook implements PublishingProcess {
public function publish(Publication $publication) {
// provedení jednotlivých úkonů
}
}
class EBook implements PublishingProcess {
public function publish(Publication $publication) {
// provedení jednotlivých úkonů
}
}
// GOOD
interface BookPublishingCommand
{
public function execute(Publication $publication);
}
class CommandManuscript implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Obdržen rukopis publikace z kategorie %s, %d stran.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class CommandTranslation implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Překlad publikace z kategorie %s, %d stran.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class CommandProofreading implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Jazyková korektura publikace " . "z kategorie %s, %d stran.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class CommandBookCover implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Příprava obálky pro publikaci " . "z kategorie %s, %d stran.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class CommandAssignISBN implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Přiřazení ISBN pro publikaci " . "z kategorie %s, %d stran.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class CommandPrint implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Tisk publikace z kategorie %s, %d stran.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class CommandPrintToPdf implements BookPublishingCommand
{
public function execute(Publication $publication)
{
printf(
"Tisk publikace z kategorie %s, " . "%d stran do PDF souboru.\n",
$publication->getCategory(),
$publication->getPageCount()
);
}
}
class Publisher
{
protected $processes = array();
public function addProcess($name, $commands)
{
$this->processes[$name] = $commands;
}
public function publish($process, Publication $publication)
{
if (!isset($this->processes[$process])) {
throw new BookPublishingException(
"Proces '" . $process . "' neexistuje."
);
}
printf("Zahájen proces: %s.\n", $process);
foreach ($this->processes[$process] as $command) {
$command->execute($publication);
}
}
}
// USAGE
$printedBookProcess = array(
new CommandManuscript(),
new CommandProofreading(),
new CommandBookCover(),
new CommandAssignISBN(),
new CommandPrint()
);
$eBookProcess = array(
new CommandTranslation(),
new CommandProofreading(),
new CommandBookCover(),
new CommandPrintToPdf()
);
$publisher = new Publisher();
$publisher->addProcess('printedBook', $printedBookProcess);
$publisher->addProcess('eBook', $eBookProcess);
$book1 = new Book('PC', 450);
$book2 = new Journal('MEDICINA', 50);
$publisher->publish('printedBook', $book1);
$publisher->publish('eBook', $book2);