/ Gists

Gists

On gists

22. Strategy

Navrhove vzory - Bohmer

strategy.php #

<?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);

On gists

20. Iterator

Navrhove vzory - Bohmer

iterator.php #

<?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));

On gists

19. Visitor

Navrhove vzory - Bohmer

visitor.php #

<?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()


On gists

14. Observe

Navrhove vzory - Bohmer

observer.php #

<?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
*/

On gists

23. Command

PHP Patterns

pic.md #


On gists

22. Memento

PHP Patterns

pic.md #


On gists

17. Command

Navrhove vzory - Bohmer

command.php #

<?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);

On gists

16. Template Method

Navrhove vzory - Bohmer

template-method.php #

<?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();

On gists

15. Mediator

Navrhove vzory - Bohmer

mediator.php #

<?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');

On gists

21. Mediator

PHP Patterns

pic.md #