Softvérové návrhové vzory

Na tejto stránke sa nachádza niekoľko návrhových vzorov implementovaných v PHP 7.

Ukážková implementácia niektorých návrhových vzorov

Adapter

<?php

class Book {
    private $author;
    private $title;
    private $year;
    function __construct($author, $title, $year) {
        $this->author = $author;
        $this->title  = $title;
        $this->year = $year;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
    function getYear() {
        return $this->year;
    }
}

class CitationAdapter {
    private $book;
    function __construct(Book $book) {
        $this->book = $book;
    }
    function getCitation() {
        return strtoupper($this->book->getAuthor()).
        " (".$this->book->getYear().") ".
        "<i>".$this->book->getTitle()."</i>";
    }
}

// client
$book = new Book("Gamma, Helm, Johnson, Vlissides", "Design Patterns", 1994);
$citation = new CitationAdapter($book);
echo $citation->getCitation();

?>

Bridge

<?php

class Book {
    private $author;
    private $title;
    function __construct($author, $title) {
        $this->author = $author;
        $this->title  = $title;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}

abstract class BridgeBook {
    private $author;
    private $title;
    private $implementation;
    function __construct($book, $implementation) {
        $this->book = $book;
        $this->implementation = $implementation;
    }
    function showAuthor() {
        return $this->implementation->showAuthor($this->book->getAuthor());
    }
    function showTitle() {
        return $this->implementation->showTitle($this->book->getTitle());
    }
}
 
class BridgeBookAuthorTitle extends BridgeBook {    
    function showAuthorTitle() {
      return $this->showAuthor().": ".$this->showTitle();
    }
}  
 
class BridgeBookTitleAuthor extends BridgeBook {    
    function showTitleAuthor() {
      return $this->showTitle() . ' bola napísaná autorom ' . $this->showAuthor();
    }
}
 
abstract class BridgeBookImpl {
    abstract function showAuthor($author);
    abstract function showTitle($title);
}

class BridgeBookCapsImpl extends BridgeBookImpl {
    function showAuthor($author) {
        return mb_strtoupper($author); 
    }
    function showTitle($title) {
        return mb_strtoupper($title);
    }
}

class BridgeBookStarsImpl extends BridgeBookImpl {
    function showAuthor($author) {
        return str_replace(" ", "*", $author);
    }
    function showTitle($title) {
        return str_replace(" ", "*", $title); 
    }
}

// client
$stars = new BridgeBookStarsImpl();
$caps = new BridgeBookCapsImpl();
$book = new Book("Gamma, Helm, Johnson, Vlissides", "Design Patterns");

$capsBridge = new BridgeBookAuthorTitle($book, $caps);
echo $capsBridge->showAuthorTitle()."\n";

$starsBridge = new BridgeBookTitleAuthor($book, $stars);
echo $starsBridge->showTitleAuthor()."\n";

?>

Builder

<?php

class Bicycle {
	private $frame;
	private $tires;
	public function setFrame($frame) {
		$this->frame = $frame;
	}
	public function setTires($tires) {
		$this->tires = $tires;
	}
	public function getFrame() {
		return $this->frame;
	}
	public function getTires() {
		return $this->tires;
	}
}

abstract class BicycleBuilder {
	protected $bicycle;
	public function getBicycle() {
		return $this->bicycle;
	}
	public function createNewBicycle() {
		$this->bicycle = new Bicycle();
	}
	abstract public function buildFrame();
	abstract public function buildTires();
}

class MountainBikeBuilder extends BicycleBuilder {
	public function buildFrame() {
		$this->bicycle->setFrame("Alloy");
	}
	public function buildtires() {
		$this->bicycle->setTires('29"');
	}
}

class RoadBikeBuilder extends BicycleBuilder {
	public function buildFrame() {
		$this->bicycle->setFrame("Carbon");
	}
	public function buildTires() {
		$this->bicycle->setTires('28"');
	}
}

class BikeMechanic {
	private $builder;
	public function setBuilder($builder) {
		$this->builder = $builder;
	}
	public function getBicycle() {
		return $this->builder->getBicycle();
	}
	public function constructBike() {
		$this->builder->createNewBicycle();
		$this->builder->buildFrame();
		$this->builder->buildTires();
	}
}

//client
$mechanic = new BikeMechanic();
$mountainBikeBuilder = new MountainBikeBuilder();
$roadBikeBuilder = new RoadBikeBuilder();

$mechanic->setBuilder($mountainBikeBuilder);
$mechanic->constructBike();
$mountainBike = $mechanic->getBicycle();
var_dump($mountainBike->getFrame());
var_dump($mountainBike->getTires());

$mechanic->setBuilder($roadBikeBuilder);
$mechanic->constructBike();
$roadBike = $mechanic->getBicycle();
var_dump($roadBike->getFrame());
var_dump($roadBike->getTires());

Chain of responsibility

<?php

interface Handler {
    public function setNext(Handler $handler);
    public function handle($request);
}

abstract class AbstractHandler implements Handler {
    private $nextHandler = null;
    public function setNext(Handler $handler) {
        $this->nextHandler = $handler;
    }
    public function handle($request) {
        if ($this->nextHandler) {
            return $this->nextHandler->handle($request);
        }
        return "No one will eat ".$request.".\n";
    }
}

class MonkeyHandler extends AbstractHandler {
    public function handle($request) {
        if ($request == "Banana") {
            return "Monkey: I'll eat the ".$request.".\n";
        } else {
            return parent::handle($request);
        }
    }
}

class SquirrelHandler extends AbstractHandler {
    public function handle($request) {
        if ($request == "Nut") {
            return "Squirrel: I'll eat the ".$request.".\n";
        } else {
            return parent::handle($request);
        }
    }
}

class DogHandler extends AbstractHandler {
    public function handle($request) {
        if ($request == "MeatBall") {
            return "Dog: I'll eat the ".$request.".\n";
        } else {
            return parent::handle($request);
        }
    }
}

// client
$animal1 = new MonkeyHandler();
$animal2 = new SquirrelHandler();
$animal3 = new DogHandler();

$animal1->setNext($animal2);
$animal2->setNext($animal3);

echo $animal1->handle("Nut");
echo $animal1->handle("Banana");
echo $animal1->handle("Spam");

?>

Composite

<?php

abstract class Graphic {
	abstract function write($indentlevel = 0);
}

class CompositeGraphic extends Graphic {
    private $graphic;
        
    function __construct() {
        $this->graphic = [];
    }
    function add($graphic) {
        $this->graphic[] = $graphic;
    }
    function write($indentlevel = 0) {
        foreach($this->graphic as $g) {
            $g->write($indentlevel + 1);
        }
    }
}

class Ellipse extends Graphic {
    private $name;
    public function __construct($name) {
        $this->name = $name;
    }
    function write($indentlevel = 0) {
        echo str_repeat('-', $indentlevel).' Ellipse '.$this->name."\n";
    }
}

class Triangle extends Graphic {
    private $name;
    public function __construct($name) {
        $this->name = $name;
    }
    function write($indentlevel = 0) {
         echo str_repeat('-', $indentlevel).' Triangle '.$this->name."\n";
    }
}

$triangle1 = new Triangle('T1');
$triangle2 = new Triangle('T2');
$triangle3 = new Triangle('T3');
$ellipse1 = new Ellipse('E1');
$ellipse2 = new Ellipse('E2');

$graphic1 = new CompositeGraphic();
$graphic1->add($triangle1);
$graphic1->add($ellipse1);

$graphic2 = new CompositeGraphic();
$graphic2->add($ellipse2);
$graphic2->add($triangle2);

$graphic3 = new CompositeGraphic();
$graphic3->add($triangle3);

$graphic2->add($graphic3);
$graphic1->add($graphic2);

$graphic1->write();

?>

Decorator

<?php

interface Car {
    function cost();
}

class Suv implements Car {
    function cost() {
        return 10000;
    }
}

abstract class CarFeature implements Car {
    protected $car;
    public function __construct(Car $car) {
        $this->car = $car;
    }
    abstract function cost();
}

class SunRoof extends CarFeature {
    function cost() {
        return $this->car->cost() + 1500;
    }
}

class HighEndWheels extends CarFeature {
    function cost() {
        return $this->car->cost() + 2000;
    }
}

// client
$basicCar = new Suv();
$carWithSunRoof = new SunRoof($basicCar);
$carWithHighEndWheels = new HighEndWheels($carWithSunRoof);

echo $carWithHighEndWheels->cost();

?>

Factory

<?php

abstract class Creator {
	protected abstract function factoryMethod();
	
	public function startFactory() {
		$mfg = $this->factoryMethod();
		return $mfg;
	}
}

interface Product {
	public function getProperties();
	
}

class TextProduct implements Product {
	public function getProperties() {
		return "this is a text";
	}
}

class GraphicsProduct implements Product {
	public function getProperties() {
		return "smile :-)";
	}
}

class TextFactory extends Creator {
	protected function factoryMethod() {
		$product = new TextProduct();
		return $product;
	}
}

class GraphicsFactory extends Creator {
	protected function factoryMethod() {
		$product = new GraphicsProduct();
		return $product;
	}
}


$graphicFactory = new GraphicsFactory();
$graphicProduct = $graphicFactory->startFactory();
var_dump($graphicProduct->getProperties());

$textFactory = new TextFactory();
$textProduct = $textFactory->startFactory();
var_dump($textProduct->getProperties());

?>

Intepreter

Calculator expressions

<?php

interface Expression {
    function evaluate(array &$variables);
}

class UnaryExpression implements Expression {
    function __construct($operator, $operand) {
        $this->operator = $operator;
        $this->operand = $operand;
    }
    
    function evaluate(array &$variables) {
        $operand = $this->operand->evaluate($variables);
        
        switch ($this->operator) {
            case "sqrt": return sqrt($operand); 
            case "log10": return log10($operand);
            case "abs": return abs($operand);
            case "sin": return sin($operand);
            case "cos": return cos($operand);
            case "tan": return tan($operand);
        }
    }
}

class BinaryExpression implements Expression {
    function __construct($operator, $leftOperand, $rightOperand) {
        $this->operator = $operator;
        $this->leftOperand = $leftOperand;
        $this->rightOperand = $rightOperand;
    }
    
    function evaluate(array &$variables) {
        $left = $this->leftOperand->evaluate($variables);
        $right = $this->rightOperand->evaluate($variables);
        
        switch ($this->operator) {
            case "+": return $left + $right; 
            case "-": return $left - $right;
            case "*": return $left * $right;
            case "/": return $left / $right;
        }
    }
}

class Variable implements Expression {
    function __construct($name) {
        $this->name = $name;
    }
    
    function evaluate(array &$variables) {
        return $variables[$this->name];
    }
}

//client
$variables = array("x" => 2, "y" => 3, "z" => 5, "a" => 1000);

$expression = new UnaryExpression(
	"abs",
	new BinaryExpression(
		"*",
		new Variable("x"),
		new BinaryExpression(
			"-",
			new Variable("y"),
			new Variable("z")
		)
	)
);

var_dump($expression->evaluate($variables));

$expression2 = new UnaryExpression(
    "log10", 
    new UnaryExpression(
        "sqrt",
        new Variable("a")
    )
);

var_dump($expression2->evaluate($variables));

?>

Logical expressions

<?php

interface LogicalExpression {
    function evaluate(array &$variables);
}

class Constant implements LogicalExpression {
    function __construct($value) {
        $this->value = $value;
    }
    
    function evaluate(array &$variables) {
        return $this->value;
    }
}

class Negation implements LogicalExpression {
    function __construct($expr) {
        $this->expr = $expr;
    }
    
    function evaluate(array &$variables) {
        return !$this->expr->evaluate($variables);
    }
}

class Conjunction implements LogicalExpression {
    function __construct($leftOperand, $rightOperand) {
        $this->leftOperand = $leftOperand;
        $this->rightOperand = $rightOperand;
    }
    
    function evaluate(array &$variables) {
        return $this->leftOperand->evaluate($variables) && $this->rightOperand->evaluate($variables);
    }
}

class Alternative implements LogicalExpression {
    function __construct($leftOperand, $rightOperand) {
        $this->leftOperand = $leftOperand;
        $this->rightOperand = $rightOperand;
    }
    
    function evaluate(array &$variables) {
        return $this->leftOperand->evaluate($variables) || $this->rightOperand->evaluate($variables);
    }
}

class Variable implements LogicalExpression {
    function __construct($name) {
        $this->name = $name;
    }
    
    function evaluate(array &$variables) {
        return $variables[$this->name];
    }
}
//client
$variables = array("x" => false, "y" => true);

$expression = new Alternative(
    new Conjunction(new Constant(true), new Variable("x")),
    new Conjunction(new Variable("y"), new Negation(new Variable("x")))
);

var_dump($expression->evaluate($variables));

?>

Regular expressions

<?php

interface RegularExpression {
    function evaluate(array &$tokens);
}

class Literal implements RegularExpression {
    function __construct($token) {
        $this->token = $token;
    }
    
    function evaluate(array &$tokens) {
        if (count($tokens) > 0) {
            if ($this->token == $tokens[0]) {
                array_shift($tokens);
                return true;
            }
        }
        return false;
    }
}

class Repetition implements RegularExpression {
    function __construct($expr) {
        $this->expr = $expr;
    }
    
    function evaluate(array &$tokens) {
        while ($this->expr->evaluate($tokens))
            ;
        
        return true;
    }
}

class Sequence implements RegularExpression {
    function __construct($firstExpr, $secondExpr) {
        $this->firstExpr = $firstExpr;
        $this->secondExpr = $secondExpr;
    }
    
    function evaluate(array &$tokens) {
        return $this->firstExpr->evaluate($tokens) && $this->secondExpr->evaluate($tokens);
    }
}

class Alternative implements RegularExpression {
    function __construct($firstExpr, $secondExpr) {
        $this->firstExpr = $firstExpr;
        $this->secondExpr = $secondExpr;
    }
    
    function evaluate(array &$tokens) {
        return $this->firstExpr->evaluate($tokens) || $this->secondExpr->evaluate($tokens);
    }
}

class Interpret {
    private $regexp;
    
    public function __construct($regexp) {
        $this->regexp = $regexp;
    }
    
    public function evaluate(array $tokens) {
        $ret = $this->regexp->evaluate($tokens);
        return $ret && count($tokens) == 0;
    }

}
//client
$interpret = new Interpret(
    new Sequence(
        new Sequence(
            new Literal("alfa"),
            new Literal("beta")
        ),
        new Repetition(
            new Alternative(
                new Literal("gama"),
                new Literal("delta")
            )
        )
    )
);

$tokens = array("alfa", "beta", "gama", "gama", "delta", "gama");
var_dump($interpret->evaluate($tokens));

?>

Memento

<?php

class BookReader {    
    private $title;   
    private $page;    
    function __construct($title, $page) {
        $this->setPage($page);
        $this->setTitle($title);
    }
    public function getPage() {
      return $this->page;
    }
    public function setPage($page) {
        $this->page = $page;
    }
    public function getTitle() {
        return $this->title;
    }
    public function setTitle($title) {
        $this->title = $title;
    }
}

class Bookmark {    
    private $title;    
    private $page;    
    function __construct(BookReader $bookReader) {
        $this->setPage($bookReader);
        $this->setTitle($bookReader);
    }
    public function getPage(BookReader $bookReader) {
        $bookReader->setPage($this->page);
    }
    public function setPage(BookReader $bookReader) {
        $this->page = $bookReader->getPage();
    }
    public function getTitle(BookReader $bookReader) {
        $bookReader->setTitle($this->title);
    }
    public function setTitle(BookReader $bookReader) {
        $this->title = $bookReader->getTitle();
    }
}

// client
$bookReader = new BookReader("Design Patterns", 103);
$bookmark = new Bookmark($bookReader);

$bookReader->setPage(104);
$bookmark->setPage($bookReader);

$bookReader->setPage(105);

$bookmark->getPage($bookReader);

?>

Observer

<?php

abstract class AbstractObserver {
    abstract function update(AbstractSubject $subject);
}

abstract class AbstractSubject {
    abstract function attach(AbstractObserver $observer);
    abstract function notify();
}

class TemperatureMeterSubject extends AbstractSubject {
    private $temperature = null;
    private $observers = [];
    public function attach(AbstractObserver $observer) {
        $this->observers[] = $observer;
    }
    public function notify() {
        foreach($this->observers as $o) {
            $o->update($this);
        }
    }
    function updateTemperature($newTemperature) {
      $this->temperature = $newTemperature;
      $this->notify();
    }
    function getTemperature() {
        return $this->temperature;
    }
}

class TemperatureDigitalObserver extends AbstractObserver {
    public function update(AbstractSubject $subject) {
        echo 'Temperature:'.$subject->getTemperature().'.';
    }
}

class TemperatureRangeObserver extends AbstractObserver {
    public function update(AbstractSubject $subject) {
        $temperature = $subject->getTemperature();
        echo ($temperature < 10 || $temperature > 40) ? 'Out of range.' : 'In range.';
    }
}


$temperatureMeter = new TemperatureMeterSubject();
$digitalObserver = new TemperatureDigitalObserver();
$rangeObserver = new TemperatureRangeObserver();

$temperatureMeter->attach($digitalObserver);
$temperatureMeter->attach($rangeObserver);

$temperatureMeter->updateTemperature(37.5);

$temperatureMeter->updateTemperature(-10);

?>

Proxy

<?php

class ProxyBookList {
    private $bookList = null;
    function addBook($book) {
        if ($this->bookList == null)
            $this->makeBookList();
        return $this->bookList->addBook($book);
    }  
    function getBook($index) {
        if ($this->bookList == null)
            $this->makeBookList();
        return $this->bookList->getBook($index);
    } 
    function makeBookList() {
        $this->bookList = new bookList();
    }
}

class BookList {
    private $books = array();
    public function getBook($index) {
        return $this->books[$index];
    }
    public function addBook(Book $book) {
        $this->books[] = $book;
    }
}

class Book {
    private $author;
    private $title;
    function __construct($title, $author) {
      $this->author = $author;
      $this->title  = $title;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}

$proxyBookList = new ProxyBookList();
$book = new Book("Design Patterns", "Gamma, Helm, Johnson, Vlissides");

$proxyBookList->addBook($book);
$outBook = $proxyBookList->getBook(0);
echo $outBook->getAuthor().': '.$outBook->getTitle(); 

?>

State

<?php

class TVException extends Exception { }

interface State {
	public function doAction();
}

class TVStartState implements State {
	public function doAction() {
		echo "TV is ON.";
	}
}

class TVStopState implements State {
	public function doAction() {
		echo "TV is OFF.";
	}
}

class TVSwitchToChannel implements State {
	private $channelNumber;
	
	public function __construct() {
		$this->setChannelNumber(1);
	}
	
	public function setChannelNumber($number) {
		$this->channelNumber = $number;
	}
	
	public function getChannelNumber() {
		return $this->channelNumber;
	}
	
	public function doAction() {
		echo "Switching to Channel number ".$this->getChannelNumber().".";
	}
}

class TVContext implements State {
	private $tvState;
	
	public function __construct(State $defaultState) {
		$this->tvState = $defaultState; //default state is OFF
	}
	
	public function setState($state) {
		if ($this->tvState instanceof TVStopState && $state instanceof TVSwitchToChannel) {
			throw new TVException("Cannot change channel on TV turned off.");
		}
		else {
			$this->tvState = $state;
		}
	}
	
	public function getState() {
		return $this->tvState;
	}
	
	public function doAction() {
		$this->tvState->doAction();
	}
}

$tvStartState = new TVStartState();
$tvStopState = new TVStopState();
$context = new TVContext($tvStopState);
$tvSwitchToChannel = new TVSwitchToChannel();
o
$context->setState($tvStartState);
$context->doAction();

$tvSwitchToChannel->setChannelNumber(2);
$context->setState($tvSwitchToChannel);
$context->doAction();

$context->setState($tvStartState);
$context->doAction();

$tvSwitchToChannel->setChannelNumber(3);
$context->setState($tvSwitchToChannel);
$context->doAction();

$context->setState($tvStopState);
$context->doAction();

?>

Strategy

<?php

class StrategyContext {
    private $strategy; 
    public function __construct($strategy) {
        $this->strategy = $strategy;
    }
    public function showBookTitle($book) {
      return $this->strategy->showTitle($book);
    }
}

interface StrategyInterface {
    public function showTitle($book);
}
 
class StrategyCaps implements StrategyInterface {
    public function showTitle($book) {
        return mb_strtoupper($book->getTitle());
    }
}

class StrategyExclaim implements StrategyInterface {
    public function showTitle($book) {
        return str_replace(' ', '!', $book->getTitle());
    }
}

class Book {
    private $author;
    private $title;
    function __construct($title, $author) {
        $this->author = $author;
        $this->title = $title;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}

$book = new Book("Design Patterns", "Gamma, Helm, Johnson, Vlissides");

$strategyContextCaps = new StrategyContext(new StrategyCaps());
$strategyContextExclaim = new StrategyContext(new StrategyExclaim());

echo $strategyContextCaps->showBookTitle($book);
echo $strategyContextExclaim->showBookTitle($book);

?>

Template Method

<?php

abstract class GenerateDocument {
    public function generate() {
        $this->step1();
        $this->step2();
    }
    abstract public function step1();
    public function step2() {
        echo 'Step2.';
    }
}

class GeneratePdf extends GenerateDocument {
    public function step1() {
        echo 'Step1Pdf.';
    }
}

$pdf = new GeneratePdf();
$pdf->generate();

?>

Visitor

<?php

abstract class Visitee {
    abstract function accept(Visitor $visitor);
}

class BookVisitee extends Visitee {
    private $author;
    private $title;
    function __construct($title_in, $author_in) {
        $this->author = $author_in;
        $this->title = $title_in;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
    function accept(Visitor $visitor) {
        $visitor->visitBook($this);
    }
}

class SoftwareVisitee extends Visitee {
    private $title;
    private $softwareCompany;
    function __construct($title, $softwareCompanyURL) {
        $this->title = $title;
        $this->softwareCompanyURL = $softwareCompanyURL;
    }
    function getSoftwareCompanyURL() {
        return $this->softwareCompanyURL;
    }
    function getTitle() {
        return $this->title;
    }
    function accept(Visitor $visitorIn) {
        $visitorIn->visitSoftware($this);
    }
}

abstract class Visitor {
    abstract function visitBook(BookVisitee $bookVisitee);
    abstract function visitSoftware(SoftwareVisitee $softwareVisitee);
}

class PlainDescriptionVisitor extends Visitor {  
    private $description = null;
    function getDescription() {
        return $this->description;
    }
    function setDescription($description) { 
        $this->description = $description;
    }
    function visitBook(BookVisitee $bookVisitee) {
        $this->setDescription($bookVisitee->getTitle().'. written by '.$bookVisitee->getAuthor());
    }
    function visitSoftware(SoftwareVisitee $softwareVisitee) {
        $this->setDescription($softwareVisitee->getTitle().' made by '.$softwareVisitee->getSoftwareCompanyURL());
    }
}

class HtmlDescriptionVisitor extends Visitor {
    private $description = null;
    function getDescription() {
        return $this->description;
    }
    function setDescription($description) { 
      $this->description = $description;
    }
    function visitBook(BookVisitee $bookVisitee) {
        $this->setDescription('<i>'.$bookVisitee->getTitle().'</i><b>'.
            $bookVisitee->getAuthor().'</b>');
    }
    function visitSoftware(SoftwareVisitee $softwareVisitee) {
        $this->setDescription('<b>'.$softwareVisitee->getTitle().'</b> '.
            '<a href="'.$softwareVisitee->getSoftwareCompanyURL().'">company website</a>');
    }
}

$book = new BookVisitee('Design Patterns', 'Gamma, Helm, Johnson, and Vlissides');
$software = new SoftwareVisitee('PHP', 'www.php.net');

$plainVisitor = new PlainDescriptionVisitor();
$book->accept($plainVisitor);
echo $plainVisitor->getDescription()."\n";
$software->accept($plainVisitor);
echo $plainVisitor->getDescription()."\n";

$htmlVisitor = new HtmlDescriptionVisitor();
$book->accept($htmlVisitor);
echo $htmlVisitor->getDescription()."\n";
$software->accept($htmlVisitor);
echo $htmlVisitor->getDescription()."\n";

?>

Privítam vaše pripomienky na email