<?php
/*
Definice
--------
Návrhový vzor Strategy definuje rodinu algoritmů, které zapouzdřuje, a umožňuje
jejich záměnu nezávisle na klientovi, který ji používá.
Pro implementaci tohoto návrhového vzoru jsou nutné následující kroky:¨
1. Definovat rozhraní, která budou muset implementovat všechny třídy z rodiny
algoritmů.
2. Implementovat minimálně 2 konkrétní implementace rozhraní z bodu 1.
3. Vytvořit způsob, kterým bude do objektu využívajícího tento vzor vložena
konkrétní implementace třídy z bodu 2.
4. Ve třídě využívající vzor Strategy vytvořit metodu, která své volání deleguje
na objekt vložený do třídy v bodě 3.
*/
interface Debugger
{
public function debug($message);
}
class DebuggerEcho implements Debugger
{
public function debug($message)
{
echo $message . PHP_EOL;
}
}
class DebuggerLog implements Debugger
{
// zapis do souboru, pro demonstraci jen s prefixem ...
public function debug($message)
{
echo 'LOG: ' . $message . PHP_EOL;
}
}
class DebuggerNull implements Debugger
{
public function debug($message)
{
}
}
// $library = new Library(new DebuggerEcho);
<?php
// Iterator
/*
Definice
--------
Návrhový vzor Iterator umožňuje sekvenčně přistupovat k prvkům složených objektů
bez zveřejnění jejich základní struktury.
Pro využití návrhového vzoru Iterator jsou nutné následující kroky:
1. Zvolit, zda má třída implementovat rozhraní Iterator, nebo rozhraní
IteratorAggregate.
2. Implementovat metody vyžadované zvoleným rozhraním.
*/
include '1.php';
class BookList implements \Iterator
{
protected $bookDefinitions = array();
protected $books = array();
protected $position = 0;
public function __construct($csvFile)
{
if (!file_exists($csvFile))
{
throw new IOException();
}
$fp = fopen($csvFile, 'r');
while (false != ($line = fgetcsv($fp, 1024, ';'))) {
$this->bookDefinitions[] = array(
'category' => $line[0],
'pagecount' => $line[1]
);
}
}
public function getBook($position)
{
if ($position > count($this->bookDefinitions)) {
throw new Exception();
}
if (!isset($this->books[$position])) {
$this->books[$position] = new Book(
$this->bookDefinitions[$position]['category'],
$this->bookDefinitions[$position]['pagecount']
);
}
return $this->books[$position];
}
public function countBooks()
{
return count($this->bookDefinitions);
}
public function current()
{
if (!isset($this->books[$this->position])) {
$this->books[$this->position] = new Book(
$this->bookDefinitions[$this->position]['category'],
$this->bookDefinitions[$this->position]['pagecount']
);
}
return $this->books[$this->position];
}
public function key()
{
return $this->position;
}
public function next()
{
$this->position++;
}
public function rewind()
{
$this->position = 0;
}
public function valid()
{
if ($this->position < count($this->bookDefinitions))
{
return true;
}
return false;
}
}
$list = new BookList('ext/books.csv');
// 1
for ($j = 0; $j < $list->countBooks(); $j++)
{
$book = $list->getBook($j);
printf("%d -> %s\n", $j, $book->getCategory());
}
echo PHP_EOL;
// 2
$list->rewind();
while ($list->valid())
{
$position = $list->key();
$book = $list->current();
printf("%d -> %s\n", $position, $book->getCategory());
$list->next();
}
echo PHP_EOL;
// 3
foreach($list as $position => $book)
{
printf("%d -> %s\n", $position, $book->getCategory());
}
// 4
class ListHelper
{
public static function displayBooks(\Traversable $list)
{
foreach($list as $position => $book)
{
printf("%d -> %s\n", $position, $book->getCategory());
}
}
}
echo PHP_EOL;
$list = new BookList('ext/books.csv');
ListHelper::displayBooks($list);
echo PHP_EOL;
// chceme iterator pouzit i na pole
$books = array(
new Book('PC', '100'),
new Book('MEDICINA', '400'),
new Book('AUTO-MOTO', '350'),
);
// takhle to nejde bze to neni Traversable
//ListHelper::displayBooks($books);
// takhle to pujde
ListHelper::displayBooks(new ArrayIterator($books));
<?php
// Visitor
/*
Definice
--------
Návrhový vzor Visitor vám umožní přidat nové operace do struktury objektů bez
změny prvků této struktury. Operace, které se mají v této struktuře provést, jsou
zapouzdřené do nové třídy.
Pro vytvoření a použití návštěvníka i v jiných situacích jsou nutné následující
kroky:
1. Analyzovat, jaké typy prvků, které chcete rozšířit o nové funkce, se nacházejí
ve struktuře objektů.
2. Definovat rozhraní pro objekt návštěvníka s deklaracemi metod visit*()
pro každý typ prvku.
3. Do každé třídy, která se používá ve struktuře objektů, přidat metodu
acceptVisitor(), která přijme objekt implementující rozhraní z bodu 2.
V této metodě zavolat příslušnou metodu visit*() objektu návštěvníka
a předat jí aktuální objekt.
4. Projít všechny objekty, na které odkazuje aktuální objekt (poduzly), a jejich
metodě acceptVisitor()předat objekt návštěvníka.
*/
interface Publication
{
// ... stávající metody rozhraní
public function acceptVisitor(Visitor $visitor);
}
interface Visitor
{
public function visitLibrary(Library $library);
public function visitRentalAction(RentalAction $rentalAction);
public function visitPublication(Publication $publication);
public function visitMember(Member $member);
}
class Library
{
// ... atributy a metody třídy
public function acceptVisitor(Visitor $visitor)
{
$visitor->visitLibrary($this);
foreach ($this->rentalActions as $rentalAction)
{
$rentalAction->acceptVisitor($visitor);
}
}
}
class RentalAction
{
// ... atributy a metody třídy
public function acceptVisitor(Visitor $visitor)
{
$visitor->visitRentalAction($this);
$this->publication->acceptVisitor($visitor);
$this->member->acceptVisitor($visitor);
}
}
class Member
{
// ... atributy a metody třídy
public function acceptVisitor(Visitor $visitor)
{
$visitor->visitMember($this);
}
}
class VisitorEcho implements Visitor
{
public function visitLibrary(Library $library)
{
print "Knihovna\n";
}
public function visitRentalAction(RentalAction $rentalAction)
{
printf(" + Publikace vypůjčená od %s", $rentalAction->getRentDate());
if ($rentalAction->isReturned()) {
printf(" do %s\n", $rentalAction->getReturnDate());
} else {
print " - dosud nevrácena\n";
}
}
public function visitPublication(Publication $publication)
{
printf(" - Kategorie: %s\n", $publication->getCategory());
printf(" - Počet stran: %d\n", $publication->getPageCount());
}
public function visitMember(Member $member)
{
printf(" - Čtenář: %s\n", $member->getName());
}
}
$visitor = new VisitorEcho();
$library->acceptVisitor($visitor);
<?php
class VisitorXMLExport implements Visitor
{
protected $doc;
protected $root;
protected $currentAction = null;
public function __construct()
{
$this->doc = new \DOMDocument('1.0', 'UTF-8');
$this->doc->formatOutput = true;
}
public function visitLibrary(Library $library)
{
$this->root = $this->doc->createElement('library');
$this->doc->appendChild($this->root);
}
public function visitRentalAction(RentalAction $rentalAction)
{
$this->currentAction = $this->doc->createElement('rentalAction');
$this->root->appendChild($this->currentAction);
$this->currentAction->setAttribute(
'rentDate',
$rentalAction->getRentDate()
);
if ($rentalAction->isReturned()) {
$this->currentAction->setAttribute(
'returnDate',
$rentalAction->getReturnDate()
);
}
}
public function visitPublication(Publication $publication)
{
$tag = $this->doc->createElement('publication');
$tag->setAttribute('category', $publication->getCategory());
$tag->setAttribute('pageCount', $publication->getPageCount());
$this->currentAction->appendChild($tag);
}
public function visitMember(Member $member)
{
$tag = $this->doc->createElement('member');
$tag->setAttribute('id', $member->getId());
$tag->setAttribute('name', $member->getName());
$this->currentAction->appendChild($tag);
}
public function asXML()
{
return $this->doc->saveXML();
}
}
$visitor = new VisitorXMLExport();
$library->acceptVisitor($visitor);
print $visitor->asXML()
<?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 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);
<?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 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');