Szybkie posty

Jeżeli coś jest wartego uwagi a nie jest to materiał na pełnoprawnwgo posta na blogu to będę starać się to umieszczać w tym miejscu.

#ajax #apache #behat #bitbacket #bootstrap #composer #cookies #cqrs #css #css flex #ct8 #curl #docker #doctrine #edukacja #enet #enp sla #filmy #firma #funkcje php #git #google #htaccess #html #inne #javascript #jedzenie #jquery #js/jquery #kawały #krypto #laravel #linux #oop #pdo #php #php wzorce narzędzia #phpmyadmin #phpspec #phpstan #phpstorm #phpunit #podcast #rabbit #redis #seo #soap #sql #symfony #szukanie po stringach w php #twig #virtual host #visual studio code #vue #wamp #windows #wino-nalewki #wyrazenia regularne #wzorce projektowe #xml #xxx #zdjecia #złote myśli
  • Szukasz po tagu: wzorce projektowe
  • Ilość rekordów w bazie: 14

wzorzec most

Przeznaczenie

Pozwala oddzielić abstrakcję obiektu od jego implementacji.

Formatter.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge;

interface Formatter
{
    public function format(string $text): string;
}

PlainTextFormatter.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge;

class PlainTextFormatter implements Formatter
{
    public function format(string $text): string
    {
        return $text;
    }
}

HtmlFormatter.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge;

class HtmlFormatter implements Formatter
{
    public function format(string $text): string
    {
        return sprintf('<p>%s</p>', $text);
    }
}

Service.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge;

abstract class Service
{
    protected Formatter $implementation;

    public function __construct(Formatter $printer)
    {
        $this->implementation = $printer;
    }

    public function setImplementation(Formatter $printer)
    {
        $this->implementation = $printer;
    }

    abstract public function get(): string;
}

HelloWorldService.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge;

class HelloWorldService extends Service
{
    public function get(): string
    {
        return $this->implementation->format('Hello World');
    }
}

PingService.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge;

class PingService extends Service
{
    public function get(): string
    {
        return $this->implementation->format('pong');
    }
}

Testy

Tests/BridgeTest.php

<?php declare(strict_types=1);

namespace DesignPatterns\Structural\Bridge\Tests;

use DesignPatterns\Structural\Bridge\HelloWorldService;
use DesignPatterns\Structural\Bridge\HtmlFormatter;
use DesignPatterns\Structural\Bridge\PlainTextFormatter;
use PHPUnit\Framework\TestCase;

class BridgeTest extends TestCase
{
    public function testCanPrintUsingThePlainTextFormatter()
    {
        $service = new HelloWorldService(new PlainTextFormatter());

        $this->assertSame('Hello World', $service->get());
    }

    public function testCanPrintUsingTheHtmlFormatter()
    {
        $service = new HelloWorldService(new HtmlFormatter());

        $this->assertSame('<p>Hello World</p>', $service->get());
    }
}

wzorzec pyłek

Wzorca projektowego pyłek (ang. flyweight) można użyć, jeśli mamy naprawdę dużo małych obiektów (małych jak muchy), które różnią się tylko podanym stanem lub wieloma danymi, które działają na podobnych danych wejściowych (lub część tego stanu jest powtarzalna).

Wyobraź sobie, że importujesz ogromny plik CSV z kilkoma tysiącami wierszy informacji o telewizorach rozmieszczonych po całej Europie. Twoim zadaniem jest sprawdzanie kondycji każdego z nich za pomocą pojedynczego połączenia z jego adresem IP i zalogowanie informacji o tym urządzeniu.

Kilka tysięcy nowych obiektów. To naprawdę sporo danych do przechowywania w pamięci RAM. Na szczęście duża część danych w wierszach jest powtarzalna. Możemy ponownie wykorzystywać obiekty, które już stworzyliśmy i pracować na nich. To esencja wzorca pyłek.

Zacznijmy od utworzenia przykładowego raportu. Mamy wiele różnych telewizorów umieszczonych w wielu lokalizacjach. Każdy z nich ma unikalny identyfikator użytkownika i kilka innych informacji. W tym przykładzie utworzymy 5 tys. wierszy danych.

<?php
class CSV {
    const COLUMNS = ["id", "uuid", "location", "resolution", "producer", "operating_system", "ip"];
    const LOCATIONS = ["Warsaw", "Berlin", "Amsterdam", "Paris"];
    const RESOLUTIONS = ["Full HD", "4K"];
    const PRODUCERS = ["LG", "Samsung", "Philips", "Sencor"];
    const OPERATING_SYSTEMS = ["Linux", "Android", "Ubuntu"];
    private $numItems;
    private $fileName;
    private $file;
    public function __construct (string $fileName, int $numItems) {
        $this->numItems = $numItems;
        $this->fileName = $fileName;
    }
    protected function createHeader (): void {
        fputcsv($this->file, self::COLUMNS);
    }
    protected function generateRandomString (int $length): string {
        return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);
    }
    protected function getRand (array $arr): string {
        $key = array_rand($arr);
        return $arr[$key];
    }
    protected function getRandIp () {
        return mt_rand(0, 255) . "." . mt_rand(0, 255) . "." . mt_rand(0, 255) . "." . mt_rand(0, 255);
    }
    protected function generateData (): void {
        for ($idx = 0; $idx < $this->numItems; $idx++) {
            fputcsv($this->file, [
                $idx + 1,
                $this->generateRandomString(rand(5, 10)),
                $this->getRand(self::LOCATIONS),
                $this->getRand(self::RESOLUTIONS),
                $this->getRand(self::PRODUCERS),
                $this->getRand(self::OPERATING_SYSTEMS),
                $this->getRandIp()
            ]);
        }
    }
    public function create (): void {
        $this->file = fopen($this->fileName, 'w');
        $this->createHeader();
        $this->generateData();
        fclose($this->file);
    }
}
$csv = new CSV("demo.csv", 5000);
$csv->create();


Nasz raport jest już gotowy. Teraz musimy dowiedzieć się, jak stworzyć aplikację, która będzie odpytywać każde urządzenie. Zauważyliśmy, że każdy ma unikalny identyfikator użytkownika i adres IP. Inne pola są dość powtarzalne i możemy je przechowywać w osobnym obiekcie. Nazwijmy to DeviceType.

<?php
namespace structural\flyweight;
class DeviceType {
    protected $location;
    protected $resolution;
    protected $producer;
    protected $operatingSystem;
    public function __construct (
        string $location,
        string $resolution,
        string $producer,
        string $operatingSystem
    ) {
        $this->location = $location;
        $this->resolution = $resolution;
        $this->producer = $producer;
        $this->operatingSystem = $operatingSystem;
    }
    public function reportType () {
        return "Working on device in {$this->location} with resolution {$this->resolution} crated by {$this->producer} and running {$this->operatingSystem}";
    }
}


Ponieważ możemy ponownie wykorzystywać obiekty, stwórzmy fabrykę, która będzie odpowiedzialna za cache'owanie ich i tworzenie nowych w razie potrzeby.

<?php
namespace structural\flyweight;
class DeviceTypeFactory {
    protected $deviceTypes = [];
    public function getType (
        string $location,
        string $resolution,
        string $producer,
        string $operatingSystem): DeviceType {
        $key = $this->getKey(
            $location,
            $resolution,
            $producer,
            $operatingSystem);
        if (!array_key_exists($key, $this->deviceTypes)) {
            $this->deviceTypes[$key] = new DeviceType(
                $location,
                $resolution,
                $producer,
                $operatingSystem
            );
        }
        return $this->deviceTypes[$key];
    }
    protected function getKey (
        string $location,
        string $resolution,
        string $producer,
        string $operatingSystem) {
        return md5(implode("_", func_get_args()));
    }
}

Rozpoznajemy każdy typ urządzenia, tworząc hash z wszystkich jego parametrów. Teraz stwórzmy nasze urządzenie.

<?php
namespace structural\flyweight;
class Device {
    protected $uid;
    protected $ip;
    protected $type;
    public function __construct (
        string $uid,
        string $ip,
        DeviceType $type
    ) {
        $this->uid = $uid;
        $this->ip = $ip;
        $this->type = $type;
    }
    public function ping () {
        echo "Checking if device {$this->uid} is active" . PHP_EOL;
        $this->type->reportType() . PHP_EOL;
        echo "Calling it on ip {$this->ip}" . PHP_EOL;
    }
}


Ten kod odpowiada za sprawdzenie stanu każdego urządzenia. Urządzenia te tworzone są za pomocą DeviceStorage, który również śledzi wszystkie utworzone obiekty.

<?php
namespace structural\flyweight;
class DeviceStorage {
    public $devices = [];
    public $deviceFactory;
    public function __construct () {
        $this->deviceFactory = new DeviceTypeFactory();
    }
    public function addDevice (
        string $uuid,
        string $location,
        string $resolution,
        string $producer,
        string $operatingSystem,
        string $ip
    ) {
        $type = $this->deviceFactory->getType(
            $location,
            $resolution,
            $producer,
            $operatingSystem
        );
        $this->devices[] = new Device($uuid, $ip, $type);
    }
    public function checkDevicesHealth () {
        foreach ($this->devices as $device) {
            $device->ping();
        }
    }
}

Złóżmy to w całość.

<?php
namespace structural\flyweight;
//require ...
$file = fopen('demo.csv', 'r');
$devicesDB = new DeviceStorage();

for ($i = 0; $row = fgetcsv($file); ++$i) {
    // Omitting file headers
    if ($i) {
        $devicesDB->addDevice(
            $row[1],
            $row[2],
            $row[3],
            $row[4],
            $row[5],
            $row[6]
        );
    }
}

fclose($file);
$devicesDB->checkDevicesHealth();
echo memory_get_usage() / 1024 / 1024 . " RAM USED";

Aplikacja z wykorzystaniem Flyweight zużyła około 2,1 MB na zestawie danych 5 tys. I 13,6 MB na zestawie danych 50 tys.

Bez użycia współużytkowanego DeviceType było to 3,3 MB dla 5k i 25,7 MB dla 50k. To ogromna oszczędność.

Pyłek jest rzadko używany w aplikacjach webowych PHP, ponieważ każde żądanie w PHP jest całkowicie niezależne. Często nie przechowujemy danych bezpośrednio w pamięci RAM, a raczej w niektórych trwałych bazach danych lub pamięci podręcznej. Niemniej jednak ten wzorzec może być całkiem przydatny w konkretnych przypadkach użycia lub aplikacjach wiersza poleceń.


Oryginał tekstu w języku angielskim przeczytasz tutaj.


Wzorzec composite

Jak widać, wszystkie jednostki naszego modelu rozszerzają klasę Unit. Użytkownik może więc być pewien, że każdy z obiektów hierarchii Unit będzie obsługiwał metodę bombardStrength(). Klasa Unit może być traktowana identycznie jak Archer. Klasy Army i TroopCarrier są kompozytami (ang. composite) — obiektami składającymi się z innych obiektów. Klasy Archer i LaserCannonUnit to liście bądź końcówki (ang. leaves), klasy reprezentujące końcowe węzły struktury drzewiastej przystosowane do obsługi podstawowych operacji hierarchii, ale nienadające się do przechowywania innych jej obiektów. Pojawia się wątpliwość, czy liście powinny implementować identyczny interfejs, co kompozyty (jak na rysunku 10.1). Na diagramie widać, że TroopCarrier i Army agregują inne jednostki, ale klasy liści również implementują wywołanie addUnit() — wrócimy do tego wkrótce. Na razie przyjrzyjmy się abstrakcyjnej klasie Unit:

<?php
abstract class Unit
{
    abstract function addUnit(Unit $unit);
    abstract function removeUnit(Unit $unit);
    abstract function bombardStrength();
}
//Mamy tu zarys podstawowej funkcjonalności wszystkich obiektów hierarchii Unit. Spójrzmy teraz,
//jak wymienione metody abstrakcyjne mogłyby być implementowane w obiektach-kompozytach:
class Army extends Unit
{
    private $units = array();
    function addUnit(Unit $unit)
    {
        if (in_array($unit, $this->units, true))
        {
            return;
        }
        $this->units[] = $unit;
    }
    function removeUnit(Unit $unit)
    {
        $this->units = array_udiff($this->units, array(
            $unit
        ) , function ($a, $b)
        {
            return ($a === $b) ? 0 : 1;
        });
    }
    function bombardStrength()
    {
        $ret = 0;
        foreach ($this->units as $unit)
        {
            $ret += $unit->bombardStrength();
        }
        return $ret;
    }
}

Metoda addUnit() klasy Army przed włączeniem do oddziału przekazywanej w wywołaniu jednostki sprawdza, czy nie posiada jej już w prywatnej tablicy jednostek. Metoda removeUnit() sprawdza w pętli (podobnie jak metoda addUnit()), czy usunąć dany obiekt Unit.

Obiekty klasy Army mogą przechowywać dowolnego rodzaju obiekty hierarchii Unit, w tym inne obiekty klasy Army lub końcówki takie jak Archer czy LaserCannonUnit. Ponieważ wszystkie jednostki mają implementować metodę bombardStrength(), implementacja tej metody w klasie Army sprowadza się do przejrzenia wszystkich obiektów zawieranych, przechowywanych w składowej $units, i sumowania wartości zwracanych z inicjowanych na ich rzecz wywołań bombardStrength(). Problematycznym aspektem wzorca pozostaje implementacja operacji wcielania i usuwania jednostek. Klasyczny wzorzec zakłada definicję metod add...() i remove...() w abstrakcyjnej klasie bazowej. Dzięki temu wszystkie klasy objęte szablonem udostępniają wspólny interfejs. Ale przez to implementacje tych m

<?php
class UnitException extends Exception
{
}
class Archer extends Unit
{
    function addUnit(Unit $unit)
    {
        throw new UnitException(get_class($this) . " to liść");
    }
    function removeUnit(Unit $unit)
    {
        throw new UnitException(get_class($this) . " to liść");
    }
    function bombardStrength()
    {
        return 4;
    }
}

Z definicji klasa Archer nie jest przewidziana do przechowywania obiektów hierarchii Unit, więc na wywołanie na rzecz obiektu Archer metody addUnit() albo removeUnit() reagujemy zgłoszeniem wyjątku. Ponieważ musielibyśmy podobną implementację przewidzieć dla wszystkich klas końcówek (liści), możemy zdecydować się na jej przeniesienie do abstrakcyjnej klasy bazowej

<?php
abstract class Unit
{
    abstract function bombardStrength();
    function addUnit(Unit $unit)
    {
        throw new UnitException(get_class($this) . " to liść");
    }
    function removeUnit(Unit $unit)
    {
        throw new UnitException(get_class($this) . " to liść");
    }
}
class Archer extends Unit
{
    function bombardStrength()
    {
        return 4;
    }
}

// utworzenie armii
$main_army = new Army();
// włączenie do niej paru jednostek
$main_army->addUnit(new Archer());
$main_army->addUnit(new LaserCannonUnit() );
// utworzenie nowej armii
$sub_army = new Army();
// zaciąg do nowej armii
$sub_army->addUnit(new Archer());
$sub_army->addUnit(new Archer());
$sub_army->addUnit(new Archer());
// wcielenie drugiej armii do pierwszej
$main_army->addUnit($sub_army);
// obliczenia siły ataku wykonywane automatycznie w tle
print "Atak z siłą: {$main_army->bombardStrength()}\n";

Do utworzonego oddziału głównego dodajemy kilka jednostek podstawowych. Proces ten powtarzamy dla drugiego utworzonego oddziału, który następnie wcielamy do pierwszego. Przy obliczaniu siły rażenia (Unit::bombardStrength()) wynikowego oddziału złożoność struktury hierarchii obiektów jest dla wywołującego zupełnie niewidoczna. Konsekwencje Jeśli Czytelnik myśli podobnie jak ja, powinien na widok kodu klasy Archer nabrać podejrzeń. Po co bowiem do klas końcówek włączamy metody addUnit() i removeUnit(), jeśli nie ma potrzeby obsługiwania operacji wcielania i usuwania jednostek? Odpowiedź tkwi w przezroczystości typu Unit. Jeśli użytkownik otrzymuje obiekt typu Unit, ma pewność, że obiekt ten implementuje metody addUnit() i removeUnit(). Uwidacznia się tu przyjęta we wzorcu Composite zasada, że klasy obiektów niepodzielnych (liści) mają interfejs identyczny z klasami kompozytów. Taka odpowiedź jest jednak mało satysfakcjonująca, ponieważ honorowanie interfejsu nie oznacza w tym przypadku bezpieczeństwa wywołania metod addUnit() czy removeUnit() na rzecz każdego z obiektów hierarchii Unit. Gdybyśmy owe metody przesunęli tak, aby były dostępne jedynie dla klas kompozytów, wtedy z kolei powstałby problem niepewności co do tego, czy otrzymany obiekt hierarchii Unit obsługuje czy nie obsługuje daną metodę. Mimo wszystko pozostawienie metod-pułapek w klasach liści to dla mnie sytuacja mało komfortowa. Nie ma tu wartości dodanej, a jedynie zamieszanie w projekcie systemu, ponieważ interfejs w zasadzie okłamuje użytkowników co do swojej własnej funkcjonalności. Moglibyśmy w prosty sposób wyeliminować tę niedogodność, wydzielając dla kompozytów ich własny podtyp CompositeUnit. Polegałoby to przede wszystkim na usunięciu metod addUnit() i removeUnit() z klasy Unit:

<?php
abstract class Unit
{
    function getComposite()
    {
        return null;
    }
    abstract function bombardStrength();
}

Zwróćmy uwagę na metodę getComposite(). Wrócimy do niej za moment. Teraz potrzebujemy abstrakcyjnej klasy definiującej metody usunięte z klasy Unit. Możemy w niej nawet przewidzieć ich implementacje domyślne:

abstract class CompositeUnit extends Unit
{
    private $units = array();
    function getComposite()
    {
        return $this;
    }
    protected function units()
    {
        return $this->units;
    }
    function removeUnit(Unit $unit)
    {
        $this->units = array_udiff($this->units, array(
            $unit
        ) , function ($a, $b)
        {
            return ($a === $b) ? 0 : 1;
        });
        function addUnit(Unit $unit)
        {
            if (in_array($unit, $this->units, true))
            {
                return;
            }
            $this->units[] = $unit;
        }
    }
}

Klasa CompositeUnit (kompozyt jednostek) choć sama w sobie nie zawiera żadnych metod abstrakcyjnych, jest deklarowana jako abstrakcyjna. Równocześnie rozszerza klasę Unit, nie definiując jej abstrakcyjnej metody bombardStrength(). Klasa Army (i wszystkie inne klasy kompozytów) może teraz rozszerzać klasę CompositeUnit. Organizację klas po tej modyfikacji ilustruje rysunek 10.2

 

Wyeliminowaliśmy irytujące i bezużyteczne implementacje metod dodawania i usuwania jednostek z klas liści, ale teraz klient musi przed wywołaniem tych metod sprawdzać, czy obiekt, na rzecz którego chce zainicjować wywołanie, jest obiektem klasy CompositeUnit. Tutaj do akcji wkracza metoda getComposite(). Domyślnie zwraca ona bowiem wartość pustą. Jedynie w klasach dziedziczących po CompositeUnit wartość zwracana to obiekt klasy CompositeUnit. Jeśli więc wywołanie tej metody zwróci obiekt, można na jego rzecz wywołać metodę addUnit(). Oto zastosowanie tej techniki z punktu widzenia użytkownika: 

<?php
class UnitScript
{
    static function joinExisting(Unit $newUnit, Unit $occupyingUnit)
    {
        $comp;
        if (!isnull($comp = $occupyingUnit->getComposite()))
        {
            $comp->addUnit($newUnit);
        }
        else
        {
            $comp = new Army();
            $comp->addUnit($occupyingUnit);
            $comp->addUnit($newUnit);
        }
        return $comp;
    }
}

Metoda joinExisting() (połącz siły) przyjmuje dwa obiekty hierarchii Unit. Pierwszy z nich reprezentuje jednostkę nowo przybyłą na dane pole, drugi — jednostkę już na tym polu przebywającą (okupującą pole planszy). Jeśli druga z tych jednostek jest kompozytem (obiektem klasy CompositeUnit), wtedy pierwszy z obiektów jest do niej dodawany. W innym przypadku tworzony jest nowy obiekt klasy Army, do którego wcielane są obie jednostki. Określanie przynależności do hierarchii klas kompozytowych odbywa się za pośrednictwem metody getComposite(). Jeśli zwróci ona obiekt, możemy wprost do niego dodawać nowe obiekty klasy Unit. Jeśli wynikiem wywołania getComposite() będzie wartość pusta, musimy utworzyć obiekt kompozytu na własną rękę, tworząc egzemplarz klasy Army i wcielając do niego obie jednostki. Model można uprościć jeszcze bardziej, wymuszając w metodzie Unit::getComposite() zwrócenie obiektu Army wypełnionego początkowo bieżącą jednostką Unit. Moglibyśmy też wrócić do poprzedniego modelu (w którym nie rozróżnialiśmy pomiędzy obiektami kompozytów a liśćmi) i zrealizować to samo w metodzie Unit::addUnit(): możemy tam utworzyć obiekt Army i dodać do niego oba obiekty Unit. To eleganckie rozwiązanie,Metoda joinExisting() (połącz siły) przyjmuje dwa obiekty hierarchii Unit. Pierwszy z nich reprezentuje jednostkę nowo przybyłą na dane pole, drugi — jednostkę już na tym polu przebywającą (okupującą pole planszy). Jeśli druga z tych jednostek jest kompozytem (obiektem klasy CompositeUnit), wtedy pierwszy z obiektów jest do niej dodawany. W innym przypadku tworzony jest nowy obiekt klasy Army, do którego wcielane są obie jednostki. Określanie przynależności do hierarchii klas kompozytowych odbywa się za pośrednictwem metody getComposite(). Jeśli zwróci ona obiekt, możemy wprost do niego dodawać nowe obiekty klasy Unit. Jeśli wynikiem wywołania getComposite() będzie wartość pusta, musimy utworzyć obiekt kompozytu na własną rękę, tworząc egzemplarz klasy Army i wcielając do niego obie jednostki. Model można uprościć jeszcze bardziej, wymuszając w metodzie Unit::getComposite() zwrócenie obiektu Army wypełnionego początkowo bieżącą jednostką Unit. Moglibyśmy też wrócić do poprzedniego modelu (w którym nie rozróżnialiśmy pomiędzy obiektami kompozytów a liśćmi) i zrealizować to samo w metodzie Unit::addUnit(): możemy tam utworzyć obiekt Army i dodać do niego oba obiekty Unit. To eleganckie rozwiązanie,

<?php
class TroopCarrier
{
    function addUnit(Unit $unit)
    {
        if ($unit instanceof Cavalry)
        {
            throw new UnitException("Transporter nie może przewozić koni");
        }
        parent::addUnit($unit);
    }
    function bombardStrength()
    {
        return 0;
    }
}

Jesteśmy tu zmuszeni do testowania typu obiektu przekazanego w wywołaniu metody addUnit() za pośrednictwem operatora instanceof. Im więcej takich jak ten przypadków specjalnych, tym wady wzorca będą dokuczliwsze. Wzorzec Composite działa najlepiej wtedy, kiedy większość komponentów to obiekty wymienialne, o zbliżonej semantyce. Kolejną kwestią jest koszt niektórych operacji w ramach wzorca. Typowym przykładem jest wywołanie Army::bombardStrength(), prowokujące kaskadę wywołań propagowanych w dół drzewa struktury jednostek zawieranych w oddziale. Przy mocno rozbudowanych drzewach z wieloma pododdziałami owo jedno wywołanie może sprowokować „w tle” istną lawinę wywołań. Co prawda koszt wykonania metody bombardStrength() nie jest obecnie wysoki, łatwo jednak sobie wyobrazić efekty skomplikowania obliczania siły ataku niektórych jednostek. Jednym ze sposobów eliminacji nawału wywołań i delegowania jest buforowanie wyników poprzednich wywołań metod obiektów zawieranych w obiektach-kompozytach, tak aby w przyszłych odwołaniach do tej wartości można było pominąć narzut wywołań. Ale wtedy trzeba pilnować aktualizacji buforowanych wartości, wdrażając strategię opróżniania buforów po operacjach na drzewie obiektów. Może to wymagać wyposażenia obiektów zawieranych w referencje do obiektów kompozytów. Wreszcie słowo o trwałości. Wzorzec Composite jest co prawda wyjątkowo elegancki, ale nie bardzo nadaje się do utrwalania zbudowanej struktury obiektów w bazie danych, a to dlatego, że całe struktury traktowane są jako pojedyncze obiekty. Aby więc skonstruować taką strukturę na podstawie informacji odczytywanych z bazy danych, trzeba posłużyć się serią kosztownych zapytań. Problem można wyeliminować, przypisując do całego drzewa identyfikator, tak aby można było jednym zapytaniem wyodrębnić z bazy danych wszystkie komponenty drzewa. Po wyodrębnieniu wszystkich obiektów trzeba będzie jednak i tak odtworzyć budowę drzewa, z zachowaniem układu obiektów podrzędnych i nadrzędnych, który również trzeba odzwierciedlić w schemacie bazy danych. Nie jest to zadanie bardzo trudne, ale mimo wszystko nieco skomplikowane. Przystosowanie wzorca Composite do baz danych jest wątpliwe, zupełnie inaczej ma się sprawa z językiem XML, a to dlatego, że w XML-u bardzo łatwo tworzyć drzewiaste struktury elementów


Wzorzec observer

<?php

interface Subscriber
{
    public function send(): void;
}

class Client implements Subscriber
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function send(): void
    {
        echo 'i got it: ' . $this->name . '<br>';
    }
}

class Subject
{
    private $subscribers = [];

    public function subscribe(Subscriber $subscriber): void
    {
        $this->subscribers[] = $subscriber;
    }

    public function startWork(): void
    {
        sleep(1);
        //code

        foreach ($this->subscribers as $subscriber) {
            $subscriber->send();
        }
    }
}

$client1 = new Client('first client');
$client2 = new Client('second client');
$client3 = new Client('third client');

$subject = new Subject();

$subject->subscribe($client1);
$subject->subscribe($client2);
$subject->subscribe($client3);

$subject->startWork();

2)Implementacja Sedno wzorca Observer (obserwator) polega na rozdzieleniu elementów użytkujących (obserwatorów) od klasy centralnej (podmiotu obserwacji). Obserwatory muszą być informowane o zdarzeniach zachodzących w podmiocie obserwacji. Równocześnie nie chcemy wprowadzać trwałych i sztywnych zależności pomiędzy podmiotem obserwacji a klasami obserwatorów. Możemy więc umożliwić obserwatorom rejestrowanie się w klasie podmiotu. W tym celu powinniśmy uzupełnić klasę Login o trzy nowe metody: rejestracji (attach()), rezygnacji (detach()) i powiadomienia (notify()), przystosowując klasę do wymogów wyróżniających podmioty obserwacji interfejsu (tutaj ma on nazwę Observable):

<?php
interface Observable
{
    function attach(Observer $observer);
    function detach(Observer $observer);
    function notify();
}

<?php
// Klasa Login…
class Login implements Observable
{
    private $observers = array();
    private $storage;
    const LOGIN_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS = 2;
    const LOGIN_ACCESS = 3;
    function attach(Observer $observer)
    {
        $this->observers[] = $observer;
    }
    function detach(Observer $observer)
    {
        $this->observers = array_filter($this->observers, function ($a) use ($observer)
        {
            return (!($a === $observer));
        });
    }
    function notify()
    {
        foreach ($this->observers as $obs)
        {
            $obs->update($this);
        }
    }
    
    //
    
}

Mamy więc klasę podmiotu utrzymującą listę obiektów obserwatorów. Obiekty te są dodawane do listy z zewnątrz poprzez wywołanie metody attach(). Rezygnacja z obserwacji i usunięcie z listy następuje w wyniku wywołania metody detach(). Z kolei wywołanie metody notify() służy jako powiadomienie obiektów obserwatorów o potencjalnie interesujących ich zdarzeniach. Implementacja tej metody sprowadza się do przejrzenia tablicy obiektów obserwatorów i wywołania na rzecz każdego z nich metody update(). Wywołanie metody rozsyłającej powiadomienia następuje we wnętrzu klasy Login, w ciele metody handleLogin():

//
function handleLogin($user, $pass, $ip)
{
    switch (rand(1, 3))
    {
        case 1:
            $this->setStatus(self::LOGIN_ACCESS, $user, $ip);
            $isvalid = true;
        break;
        case 2:
            $this->setStatus(self::LOGIN_WRONG_PASS, $user, $ip);
            $isvalid = false;
        break;
        case 3:
            $this->setStatus(self::LOGIN_USER_UNKNOWN, $user, $ip);
            $isvalid = false;
        break;
    }
    $this->notify();
    return $isvalid;
}

Zdefiniujmy interfejs klas obserwatorów:

interface Observer {
 function update(Observable $observable);
}

Do listy obserwatorów można dodawać (za pośrednictwem metody attach() klasy podmiotu obserwacji) dowolne obiekty, które implementują interfejs Observable. Tak wygląda tworzenie konkretnego egzemplarza:

<?php
class SecurityMonitor implements Observer
{
    function update(Observable $observable)
    {
        $status = $observable->getStatus();
        if ($status[0] == Login::LOGIN_WRONG_PASS)
        {
            // wyślij wiadomość do administratora…
            print __CLASS__ . "\twysyłam wiadomość do administratora\n";
        }
    }
}
$login = new Login();
$login->attach(new SecurityMonitor());

Zwróćmy uwagę, jak obiekt obserwatora odwołuje się do egzemplarza klasy Observable (podmiotu obserwacji) w celu pozyskania dodatkowych informacji o zdarzeniu. Metody, za pośrednictwem których obiekty obserwatorów mogłyby dowiadywać się o stanie, powinny zostać udostępnione właśnie w klasie podmiotu obserwacji. W tym przypadku klasa podmiotu ma zdefiniowaną metodę getStatus(), dzięki której obiekty obserwatorów mogą dowiadywać się o bieżącym stanie obiektu obserwowanego. Pojawia się tutaj pewien problem. Otóż w wywołaniu metody Login::getStatus() klasa SecurityMonitor bazuje na wiedzy o klasie Login, na której nie powinna polegać. Przecież w wywołaniu otrzymuje obiekt Observable, ale nie ma żadnej gwarancji, że będzie to właśnie obiekt Login. Mamy tu kilka możliwości: możemy rozszerzyć interfejs Observable tak, aby zawierał w sobie deklarację metody getStatus(), i możemy od razu przemianować interfejs na ObservableLogin, sygnalizując, że ma związek z klasami Login. Możemy też utrzymać ogólny interfejs Observable i obarczyć klasy Observable odpowiedzialnością za to, aby podmioty obserwacji były odpowiedniego typu. Możemy wtedy złożyć na nie również zadanie kojarzenia się z podmiotami obserwacji. Ponieważ będziemy mieć więcej niż jeden typ Observer, a zamierzamy zaimplementować przy okazji czynności porządkowe wspólne dla wszystkich podtypów, możemy od razu udostępnić abstrakcyjną klasę bazową:

<?php
abstract class LoginObserver implements Observer
{
    private $login;
    function __construct(Login $login)
    {
        $this->login = $login;
        $login->attach($this);
    }
    function update(Observable $observable)
    {
        if ($observable === $this->login)
        {
            $this->doUpdate($observable);
        }
    }
    abstract function doUpdate(Login $login);
}

Klasa LoginObserver wymaga do konstrukcji obiektu typu Login. W konstruktorze zachowuje sobie referencję obiektu i wywołuje własną metodę Login::attach(). W wywołaniu update() następuje sprawdzenie, czy przekazany obiekt Observable jest w istocie referencją obserwowanego podmiotu, po czym dochodzi do wywołania metody szablonowej doUpdate(). Teraz możemy utworzyć cały zestaw obiektów LoginObserver, z których każdy będzie operował na obiekcie Login, a nie na dowolnym obiekcie implementującym nasz stary interfejs Observable:

<?php
class SecurityMonitor extends LoginObserver
{
    function doUpdate(Login $login)
    {
        $status = $login->getStatus();
        if ($status[0] == Login::LOGIN_WRONG_PASS)
        {
            // wysłanie wiadomości do administratora
            print __CLASS__ . ":\twysyłam wiadomość do administratora\n";
        }
    }
}

class GeneralLogger extends LoginObserver
{
    function doUpdate(Login $login)
    {
        $status = $login->getStatus();
        // dodanie danych do rejestru
        print __CLASS__ . ":\tdodaję dane logowania do rejestru\n";
    }
}
class PartnershipTool extends LoginObserver
{
    function doUpdate(Login $login)
    {
        $status = $login->getStatus();
        // sprawdzenie adresu IP
        // ustawienie ciasteczka dla dopuszczonego IP
        print __CLASS__ . ":\tustawiam ciasteczko dla dopuszczonego IP\n";
    }
}
//Tworzenie i podłączanie obserwatorów LoginObserver jest teraz wykonywane w czasie konkretyzacji obiektów:
$login = new Login();
new SecurityMonitor($login);
new GeneralLogger($login);
new PartnershipTool($login);

 


Wzorzec mediator

<?php

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

interface Mediator {

    public function send(string $name, string $message): void;
}

class ConcreteMediator implements Mediator {

    private $colleagues = [];

    public function colleagueRegister(Colleague $colleague): void {
        $colleague->registerMediator($this);
        $this->colleagues[$colleague->getName()] = $colleague;
    }

    public function send(string $name, string $message): void {
        $this->colleagues[$name]->getMessage($message);
    }

}

class Colleague {

    private $mediator;
    private $name;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function registerMediator(Mediator $mediator): void {
        $this->mediator = $mediator;
    }

    public function getName() {
        return $this->name;
    }

    public function send(string $name, string $message): void {
        echo "Przesyłanie wiadomości od " . $this->name . " do " . $name . ": " . $message . '<br>';

        $this->mediator->send($name, $message); /// Rzeczywista komunikacja odbywa się za pośrednictwem mediatora!!!
    }

    public function getMessage(string $message): void {
        echo "Wiadomość odebrana przez " . $this->name . ": " . $message . '<br>';
    }

}

$michael = new Colleague("michael");
$john = new Colleague("john");
$lucy = new Colleague("lucy");


$mediator = new ConcreteMediator();
$mediator->colleagueRegister($michael);
$mediator->colleagueRegister($john);
$mediator->colleagueRegister($lucy);

$lucy->send('john', "Hello world.");

echo '<br>';

$michael->send('lucy', "Witaj!");
echo '<br>';


Wzorzec prototype

<?php
$timeStart = microtime(true);

function randomDateTime() {
    $timestamp = mt_rand(1, time());
    $randomDate = date("d M Y", $timestamp);
    return new DateTime($randomDate);
}

abstract class Solder {
    protected $name;
    protected $secondName;
    protected $rank = 'szeregowy';
    protected $uniformColor;
    protected $force;
    protected $flair;
    protected $courage;
    protected $life;
    protected $dateOfBirth;

//    public function __construct(string $name, string $secondName, string $uniformColor, int $force, int $flair, int $courage, int $life, DateTime $dateOfBirth) {
//        $this->name = $name;
//        $this->secondName = $secondName;
//        $this->uniformColor = $uniformColor;
//        $this->force = $force;
//        $this->flair = $flair;
//        $this->courage = $courage;
//        $this->life = $life;
//        $this->dateOfBirth = $dateOfBirth;
//    }

    public function getName() {
        return $this->name;
    }

    public function getSecondName() {
        return $this->secondName;
    }

    public function getRank() {
        return $this->rank;
    }

    public function getUniformColor() {
        return $this->uniformColor;
    }

    public function getForce() {
        return $this->force;
    }

    public function getFlair() {
        return $this->flair;
    }

    public function getCourage() {
        return $this->courage;
    }

    public function getLife() {
        return $this->life;
    }

    public function getDateOfBirth() {
        return $this->dateOfBirth;
    }

    public function setName($name) {
        $this->name = $name;
        return $this;
    }

    public function setSecondName($secondName) {
        $this->secondName = $secondName;
        return $this;
    }

    public function setRank($rank) {
        $this->rank = $rank;
        return $this;
    }

    public function setUniformColor($uniformColor) {
        $this->uniformColor = $uniformColor;
        return $this;
    }

    public function setForce($force) {
        $this->force = $force;
        return $this;
    }

    public function setFlair($flair) {
        $this->flair = $flair;
        return $this;
    }

    public function setCourage($courage) {
        $this->courage = $courage;
        return $this;
    }

    public function setLife($life) {
        $this->life = $life;
        return $this;
    }

    public function setDateOfBirth($dateOfBirth) {
        $this->dateOfBirth = $dateOfBirth;
        return $this;
    }

}

class StrongSolder extends Solder {
    protected $rank = 'general';
}

class CleverSolder extends Solder {
    
}

$strongSolder = (new StrongSolder())->setName('new janusz')->setSecondName('kowalsi')->setUniformColor('blul')->setForce(90)->setFlair(80)->setCourage(70)->setLife(49)->setDateOfBirth(randomDateTime());
$cleverSolder = (new CleverSolder())->setName('new jan')->setSecondName('kowalsi')->setUniformColor('blul')->setForce(90)->setFlair(80)->setCourage(70)->setLife(49)->setDateOfBirth(randomDateTime());

$strongSolders = [];
$cleversSolders = [];

for ($i = 0; $i < 10000; $i++) {
    $strongSolders[] = (new StrongSolder())
            ->setName('new janusz' . $i)
            ->setSecondName('kowalsi' . $i)
            ->setUniformColor('blul')
            ->setForce(90)
            ->setFlair(80)
            ->setCourage(70)
            ->setLife(49)
            ->setDateOfBirth(randomDateTime());

    $cleversSolders[] = (new CleverSolder())
            ->setName('new jan' . $i)
            ->setSecondName('kowalsi' . $i)
            ->setUniformColor('blul')
            ->setForce(90)
            ->setFlair(80)
            ->setCourage(70)
            ->setLife(49)
            ->setDateOfBirth(randomDateTime());
}

$timeEnd = microtime(true);
$executionTime = ($timeEnd - $timeStart);

echo '<h3><b>czas normalne tworzenie obiekto</b> ' . round($executionTime * 1000) . ' milliseconds</h3>';
echo '<h3><b>czas normalne tworzenie obiektow</b> ' . $executionTime . ' mikro</h3>';
echo '<h3><b>czas normalne tworzenie obiektow</b> ' . $executionTime/60 . ' sec</h3>';

$timeStart = microtime(true);

for ($i = 0; $i < 10000; $i++) {
    $strongSolders[] = clone $strongSolder
                    ->setName('new janusz' . $i)
                    ->setSecondName('kowalsi' . $i)
                    ->setUniformColor('blul')
                    ->setForce(90)
                    ->setFlair(80)
                    ->setCourage(70)
                    ->setLife(49)
                    ->setDateOfBirth(randomDateTime())
    ;
    $cleversSolders[] = clone $cleverSolder
                    ->setName('new jan' . $i)
                    ->setSecondName('kowalsi' . $i)
                    ->setUniformColor('blul')
                    ->setForce(90)
                    ->setFlair(80)
                    ->setCourage(70)
                    ->setLife(49)
                    ->setDateOfBirth(randomDateTime())
    ;
}
//printMemory();
//
//function printMemory() {
//    $mem_usage = memory_get_usage();
//    $mem_peak = memory_get_peak_usage();
//    echo 'The script is now using: <strong>' . round($mem_usage / 1024) . 'KB</strong> of memory.<br>';
//    echo 'Peak usage: <strong>' . round($mem_peak / 1024) . 'KB</strong> of memory.<br><br>';
//}
echo '<br><br>';
$timeEnd = microtime(true);
$executionTime = ($timeEnd - $timeStart);

echo '<h3><b>czas klonowanie</b> ' . round($executionTime * 1000) . ' milliseconds</h3>';
echo '<h3><b>czas klonowanie</b> ' . $executionTime . ' mikro</h3>';
echo '<h3><b>czas klonowanie</b> ' . $executionTime/60 . ' sec</h3>';

Wzorzec dekorator

<?php
interface CarInterface {
    public function calcualtePrice(): int;
}

abstract class CarDecorator implements CarInterface {
    protected $car;
    public function __construct(CarInterface $car) {
        $this->car = $car;
    }
}

Class Car implements CarInterface {
    public function calcualtePrice(): int {
        return 5000;
    }
}

Class CarWithAirConditioning extends CarDecorator {
    public function calcualtePrice(): int {
        return $this->car->calcualtePrice() + 101;
    }
}

Class CarWithSunRoof extends CarDecorator {
    public function calcualtePrice(): int {
        return $this->car->calcualtePrice() + 70;
    }
}

Class CarWith5Skin extends CarDecorator {
    public function calcualtePrice(): int {
        return $this->car->calcualtePrice() + 111;
    }
}

Class CarWithCamera extends CarDecorator {
    public function calcualtePrice(): int {
        return $this->car->calcualtePrice() + 52;
    }
}

$car1 = new CarWithCamera(new CarWithAirConditioning(new Car()));
var_dump($car1->calcualtePrice());

$car2 = new CarWithAirConditioning(new CarWith5Skin(new Car()));
var_dump($car2->calcualtePrice());

$car3 = new Car();
$car3 = new CarWithAirConditioning($car3);
$car3 = new CarWithSunRoof($car3);
$car3 = new CarWith5Skin($car3);
$car3 = new CarWithCamera($car3);
var_dump($car3->calcualtePrice());

Wzorzec adapter

<?php

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

interface ImageResizeInterface {

    public function setImage(string $fileName);

    public function resizeTo(string $width, string $height);

    public function resizeHeightByWidth(string $width);

    public function save();
}

class ResizeImage implements ImageResizeInterface {

    public function setImage(string $fileName) {
        //code
    }

    public function resizeTo(string $width, string $height) {
        //code
    }

    public function resizeHeightByWidth(string $width) {
        //code
    }

    public function save() {
        //code
    }

}

interface ImageInterface {

    public function setImageToResize(string $fileName);

    public function resizeImageTo(string $imageWidth, string $imageHeight);

    public function resizeImageHeightByWidth(string $width);

    public function saveImage();
}

class ImageResize implements ImageInterface {

    public function setImageToResize(string $fileName) {
        //code
    }

    public function resizeImageTo(string $imageWidth, string $imageHeight) {
        //code
    }

    public function resizeImageHeightByWidth(string $width) {
        //code
    }

    public function saveImage() {
        //code
    }

}

class ImageAdapter implements ImageResizeInterface {

    private $image;

    public function __construct(ImageResize $image) {
        $this->image = $image;
    }

    public function setImage(string $fileName) {
        $this->image->setImageToResize($fileName);
    }

    public function resizeTo(string $width, string $height) {
        $this->image->resizeImageTo($width, $height);
    }

    public function resizeHeightByWidth(string $width) {
        $this->image->resizeImageHeightByWidth($width);
    }

    public function save() {
        $this->image = saveImage();
    }

}

$imageAdapter = new ImageAdapter(new ImageResize());


var_dump($imageAdapter);

2//

<?php

//Book
class Book {

    private $author;
    private $title;

    function __construct(string $author, string $title) {
        $this->author = $author;
        $this->title = $title;
    }

    function getAuthor(): string {
        return $this->author;
    }

    function getTitle(): string {
        return $this->title;
    }

}

//book adapter
class BookAdapter {

    private $book;

    function __construct(Book $book) {
        $this->book = $book;
    }

    function getAuthorAndTitle(): string {
        return $this->book->getTitle() . ' by ' . $this->book->getAuthor();
    }

}

// client
$book = new Book("J.R.R. Tolkien", "Władca Pierścieni:");
$bookAdapter = new BookAdapter($book);

echo ('Author and Title: ' . $bookAdapter->getAuthorAndTitle());

Fabryka abstrakcyjna

<?php

interface CarType {

    CONST POTROL = 'petrol';
    CONST DIESEL = 'desiel';

}

class CarsFactory {

    public function createDesiel() {
        return new Car(CarType::DIESEL, '1.7', false);
    }

    public function createFastBigCar() {
        return new Car(CarType::POTROL, '3.7', false);
    }

    public function createFastSmallCar() {
        return new Car(CarType::POTROL, '1.0', true);
    }

}

Class Car {

    private $engine;
    private $capacity;
    private $turbo;

    public function __construct(string $engine, string $capacity, bool $turbo) {
        $this->engine = $engine;
        $this->capacity = $capacity;
        $this->turbo = $turbo;
    }

}

$factory = new CarsFactory();

$diesel = $factory->createDesiel();
$fastBigCar = $factory->createFastBigCar();
$fastSmallCar = $factory->createFastSmallCar();

var_dump($diesel, $fastBigCar, $fastSmallCar);

Iterator w php

https://www.php.net/manual/en/class.iteratoraggregate.php

https://www.php.net/manual/en/class.arrayiterator.php

class CarsIterator implements Iterator {
	private $position = 0;
	private $cars = ['BMW', 'Audi', 'KIA', 'Opel', 'Ford'];

	public function current() {
		return $this->cars[$this->position];
	}

	public function key() {
		return $this->position;
	}

	public function next() {
		$this->position++;
	}

	public function rewind() {
		$this->position = 0;
	}

	public function valid() {
		if (isset($this->cars[$this->position])) {
			return true;
		}

		return false;
	}
	public function reverse() {
		$this->cars = array_reverse($this->cars, true);
	}
}

$tests = new CarsIterator();

var_dump($tests->current());

$tests->next();
$tests->next();
$tests->next();
$tests->next();
$tests->next();

var_dump($tests->current());

Wzorzec facade(fasada)

Wydzielanie złożonych funkcji  np do jednej klasy co ukrywa szczegóły i pozwala w łatwy sposób uruchamiać złozone funkcjonalności. 

Wyobraźmy sobie, że struktura i treść kodu są jeszcze bardziej złożone i że przez to nie ma szans na ich przepisanie na własną rękę — pozostaje nam jedynie korzystanie z rozwiązania kalekiego, ale gotowego. Aby skonwertować plik o zawartości: 234-bluza_damska 55 532-kapelusz_męski 55 na postać tablicy obiektów, musimy wywołać wszystkie z prezentowanych funkcji (dla uproszczenia pomijamy przy tym wyodrębnianie z pliku liczby kończącej wiersz, reprezentującej cenę artykułu):

<?php
$lines = getProductFileLines('test.txt');
$objects = array();
foreach ($lines as $line)
{
    $id = getIDFromLine($line);
    $name = getNameFromLine($line);
    $objects[$id] = getProductObjectFromID($id, $name);
}

Jeśli będziemy w swoim projekcie stosować powyższą procedurę odczytu plików, zwiążemy ściśle kod projektu z kodem podsystemu wczytywania plików. Jeśli potem ów podsystem ulegnie zmianom albo zdecydujemy się na skorzystanie z innego podsystemu w jego miejsce, staniemy w obliczu problemu rozległych modyfikacji kodu projektu. Trzeba by więc powołać do życia bramkę pomiędzy tym podsystemem a resztą projektu.

Implementacja Oto prosta klasa udostępniająca interfejs do kodu proceduralnego, z którym borykaliśmy się w poprzednim punkcie:

<?php
class ProductFacade
{
    private $products = array();
    function __construct($file)
    {
        $this->file = $file;
        $this->compile();
    }
    private function compile()
    {
        $lines = getProductFileLines($this->file);
        foreach ($lines as $line)
        {
            $id = getIDFromLine($line);
            $name = getNameFromLine($line);
            $this->products[$id] = getProductObjectFromID($id, $name);
        }
    }
    function getProducts()
    {
        return $this->products;
    }
    function getProduct($id)
    {
        if (isset($this->products[$id]))
        {
            return $this->products[$id];
        }
        return null;
    }
}

Z punktu widzenia użytkownika tego kodu dostęp do obiektów Product generowanych na podstawie pliku rejestru produktów jest znacznie uproszczony:

$facade = new ProductFacade('test.txt');
$facade->getProduct(234);

Wzorzec strategia

1/

<?php
interface AbstractStrategy{
    function task();
}
class ConcreteStrategy1 implements AbstractStrategy{
    public function task() {
        echo "Strategy 1";
    }
}
class ConcreteStrategy2 implements AbstractStrategy{
    public function task() {
        echo "Strategy 2";
    }
}
class Context{
    private $strategy;
 
    public function setStrategy(AbstractStrategy $obj) {
        $this->strategy=$obj;
    }
    public function getStrategy() {
        return $this->strategy;
    }
}
// testy
$obj = new Context();
$obj->setStrategy(new ConcreteStrategy1);
$obj->getStrategy()->task(); // wyswietla „Strategy 1”
$obj->setStrategy(new ConcreteStrategy2);
$obj->getStrategy()->task(); // wyswietla „Strategy 2”
?>

2/

<?php
 
interface Tax{
    public function count($net);
}
 
class TaxPL implements Tax{
    public function count($net) {
        return 0.23*$net;
    }
}
 
class TaxEN implements Tax{
    public function count($net) {
        return 0.15*$net;
    }
}
 
class TaxDE implements Tax{
    public function count($net) {
        return 0.3*$net;
    }
}
 
class Context{
    private $tax;
 
    /**
     * @return mixed
     */
    public function getTax()
    {
        return $this->tax;
    }
 
    /**
     * @param mixed $tax
     */
    public function setTax(Tax $tax)
    {
        $this->tax = $tax;
    }
 
}
 
// testy
$tax = new Context();
$tax->setTax(new TaxPL());
echo $tax->getTax()->count(100); // wyswietla "23"
$tax->setTax(new TAXEN());
echo $tax->getTax()->count(100); // wyswietla "15"
$tax->setTax(new TAXDE());
echo $tax->getTax()->count(100); // wyswietla "30"
 
?>
<?php

interface Promotion {

    public function getPriseAfterPromotion(float $price): float;
}

class BlackFriday implements Promotion {

    public function getPriseAfterPromotion(float $price): float {
        return $price * 0.2;
    }

}

class SpanishKangaroosFestival implements Promotion {

    public function getPriseAfterPromotion(float $price): float {
        $discount = floor($price / 10) * 2;
        return $price - $discount;
    }

}

class Checkout {

    /**
     * @var type 
     */
    private $price;

    /**
     * @var Promotion 
     */
    private $promotion;

    /**
     * @param float $price
     */
    public function setPrice(float $price): void {
        $this->prise = $price;
    }

    /**
     * @param Promotion $promition
     */
    public function setPromotion(Promotion $promition): void {
        $this->promition = $promition;
    }

    /**
     * @return float
     */
    public function getTotalPrice(): float {
        return $this->promition->getPriseAfterPromotion($this->prise);
    }

}

$checkout = new Checkout();
$checkout->setPrice(22.333);
$checkout->setPromotion(new BlackFriday());

echo $checkout->getTotalPrice() . ' zl';
echo '<br><br>';

$checkout2 = new Checkout();
$checkout2->setPrice(120.9);
$checkout2->setPromotion(new SpanishKangaroosFestival());

echo $checkout2->getTotalPrice() . ' zl';

Wzorzec komendy

<?php
//Programowanie obiektowe w języku PHP5 / Moduł 5. SOLID / Cz. 2
require_once 'lib/logic/Aplication.php';
require_once 'lib/logic/AplicationModel.php';

interface Command {
  public function execute($parameters);
  public function printHelp();
}

class Action {
  public function execute() {}
}

class HellowAction extends Action {
  public function __construct($app) {
    $this->app = $app;
  }

  function execute() {
    $this->app->hellow();
  }
}

class LogoutAction extends Action {
  public function __construct($app) {
    $this->app = $app;
  }

  function execute() {
    $this->app->logout();
  }
}

//wzorzec komendy

class RequestProcess {
  public function process() {
    session_start();
    $app = new Aplication;
    $aplicationmodel = new AplicationModel;

    $actions['hellow'] = new HellowAction($app);
    $actions['logout'] = new LogoutAction($app);

    include 'template/header.php';
    include 'template/menu.php';

    $actionName = $_GET['action'];
    $action = $actions[$actionName];

    $action->execute();

    include 'template/footer.php';

  }
}
?>

To samo bez zastosowania wzoraca:

<?php

require_once 'lib/logic/Aplication.php';
require_once 'lib/logic/AplicationModel.php';

class RequestProcess {

  function __construct() {

  }

  public function process() {
    session_start();
    $app = new Aplication;
    $aplicationmodel = new AplicationModel;

    include 'template/header.php';
    include 'template/menu.php';

    if (isset($_GET['action']) && $_GET['action'] == 'hellow') {
      $app->Hellow();
    } elseif (isset($_GET['action']) && $_GET['action'] == 'logout') {
      $app->logout();
    }

    //z

    include 'template/footer.php';

  }
}
?>

statyczny budowniczy

<?php
//Programowanie obiektowe w języku PHP5 / Moduł 4. Algorytmy i struktury danych / Cz. 3
// wzorzec budowniczy
class HTMLBuilder {
  private $html;

  private function __construct($html) {
    $this->html = $html;
  }

  public static function begin() {
    $builder = new HTMLBuilder('<html><head></head><body>');
    return $builder;
  }

  public function p($paragraph) {
    $this->html .= '<p>' . $paragraph . '</p>';
    return $this;
  }

  public function hr() {
    $this->html .= '<hr>';
    return $this;
  }

  public function pre($contents) {
    $safecontents = htmlspecialchars($contents);
    $this->html .= "<pre>$safecontents</pre>";
    return $this;
  }

  public function end() {
    $this->html .= '</body></html>';
    return $this->html;
  }

}

$test = HTMLBuilder::begin()

  ->p('paragraf')
  ->hr()
  ->pre('class HTMLBuilder')
  ->p('kolejny paragraf')
  ->end();

echo '<pre>';
var_dump($test);
echo '</pre>';