Kcko
3/28/2020 - 9:03 PM

4. Abstract Factory (Abstraktní továrna)

<?php

//include "1.php";

/*
    Pro vytvoření jednotlivých komponent bez nutnosti zadání jejich konkrétní implementace použijeme návrhový vzor Abstract Factory.
    Návrhový vzor Abstract Factory poskytuje rozhraní pro vytvoření skupin podobných nebo souvisejících objektů bez pojmenování konkrétních tříd.
*/

/* 
    Definice
    Návrhový vzor Abstract Factory poskytuje rozhraní pro vytvoření skupin podobných
    nebo souvisejících objektů bez pojmenování konkrétních tříd.
    K dosažení tohoto cíle je nutné provést následující kroky:
    1. Definovat rozhraní továrny, kde pro každý objekt ze skupiny objektů bude
    deklarovaná abstraktní metoda. K tomu lze použít buď abstraktní třídu,
    nebo rozhraní.
    2. Implementovat abstraktní třídy, jež reprezentují jednotlivé objekty ze skupiny
    objektů.
    3. Implementovat libovolné potomky těchto abstraktních tříd.
    4. Implementovat jednu nebo více tříd (továren), které tyto objekty vytvářejí.
*/

// ABSTRAKTNI TRIDY

abstract class Table
{
    protected $rows = array();
    protected $header = null;
    public function addRow(Row $row)
    {
        $this->rows[] = $row;
    }
    public function setHeader(Header $header)
    {
        $this->header = $header;
    }
    abstract public function show();
}


abstract class Row
{
    protected $cells = array();

    public function addCell(Cell $cell)
    {
        $this->cells[] = $cell;
    }

    abstract public function show();
}


abstract class Header extends Row 
{
}


abstract class Cell
{
    protected $content = null;
    public function __construct($content)
    {
        $this->content = $content;
    }

    abstract public function show();
}



// INTERFACE TABLE FACTORY
interface TableFactory
{
    public function createCell($content);
    public function createRow();
    public function createHeader();
    public function createTable();
}


class HtmlCell extends Cell
{
    public function show()
    {
        printf(" <td>%s</td>\n", $this->content);
    }
}

class HtmlRow extends Row
{
    public function show()
    {
        print " <tr>\n";
        foreach ($this->cells as $cell) {
            $cell->show();
        }
        print " \n";
    }
}

class HtmlHeader extends Header
{
    public function show()
    {
        print " <tr style=\"font-weight: bold;\">\n";

        foreach ($this->cells as $cell) {
            $cell->show();
        }

        print " </tr>\n";
    }
}

class HtmlTable extends Table
{
    public function show()
    {
        print "<table border=\"1\">\n";
        $this->header->show();
        foreach ($this->rows as $row) {
            $row->show();
        }
        print "\n";
    }
}

// USAGE
$table = new HtmlTable();
$header = new HtmlHeader();
$header->addCell(new HtmlCell('Sloupec 1'));
$header->addCell(new HtmlCell('Sloupec 2'));
$table->setHeader($header);

$row = new HtmlRow();
$row->addCell(new HtmlCell('Pozice [1:1]'));
$row->addCell(new HtmlCell('Pozice [1:2]'));
$table->addRow($row);
$table->show();

/*

<table border="1">
    <tr style="font-weight: bold;">
        <td>Sloupec 1</td>
        <td>Sloupec 2</td>
    </tr>
    <tr>
        <td>Pozice [1:1]</td>
        <td>Pozice [1:2]</td>
    </tr>
</table>

Tím jste splnili jednu část úlohy – dokážete vytvořit tabulku HTML, které může-
te z vnějšku předat obsah. Nové objekty jste ale vytvářeli pomocí operátoru new,
kvůli čemuž je kód pevně svázaný s konkrétní implementací. Pokud byste nyní
chtěli změnit tabulku HTML na textový výpis do konzoly, museli byste změnit
skoro každý řádek zdrojového kódu. Tomu se můžete vyhnout pomocí defino-
vaného rozhraní TableFactory. Je tedy nutné přidat další třídu, která toto roz-
hraní implementuje a vrátí konkrétní implementaci zodpovědnou za tabulku
HTML. Vytvořte tedy novou třídu HtmlTableFactory, která implementuje roz-

 */


// ABSTRACT FACTORY - OUR GOAL
class HtmlTableFactory implements TableFactory
{
    public function createCell($content)
    {
        return new HtmlCell($content);
    }
    public function createRow()
    {
        return new HtmlRow();
    }
    public function createHeader()
    {
        return new HtmlHeader();
    }
    public function createTable()
    {
        return new HtmlTable();
    }
}

// USAGE
$factory = new HtmlTableFactory();
$table = $factory->createTable();
$header = $factory->createHeader();
$header->addCell($factory->createCell('Sloupec 1'));
$header->addCell($factory->createCell('Sloupec 2'));
$table->setHeader($header);

$row = $factory->createRow();
$row->addCell($factory->createCell('Pozice [1:1]'));
$row->addCell($factory->createCell('Pozice [1:2]'));
$table->addRow($row);
$table->show();

/*
Po provedení tohoto skriptu dostanete naprosto stejný výsledek jako v předcho-
zím příkladu.
Jako poslední zbývá implementovat třídu, která vypíše seznam publikací knihov-
ny na základě údajů z pole. Tato třída má pro vytvoření elementů tabulky pou-
žít libovolnou třídu implementující rozhraní TableFactory. Tím oddělíte třídu,
která vytváří seznam, od zobrazení ve formě kódu jazyka HTML. Konkrétní
továrnu, která bude zobrazovat tabulku se seznamem, předáte nové třídě při
tvorbě její instance, přičemž znovu využijete princip vkládání závislostí (angl.
Dependency Injection).
*/

class PublicationList
{
    protected $tableFactory = null;
    public function __construct(TableFactory $tf)
    {
        $this->tableFactory = $tf;
    }
    public function displayTable($data)
    {
        $table = $this->tableFactory->createTable();

        $header = $this->tableFactory->createHeader();
        $header->addCell($this->tableFactory->createCell('Kategorie'));
        $header->addCell($this->tableFactory->createCell('Počet stran'));

        $table->setHeader($header);

        foreach ($data as $line) {
            $row = $this->tableFactory->createRow();
            foreach ($line as $field) {
                $cell = $this->tableFactory->createCell($field);
                $row->addCell($cell);
            }
            $table->addRow($row);
        }
        $table->show();
    }
}

// USAGE
$publications = array(
    array('PC', 100),
    array('PC', 380),
    array('medicína', 250),
    array('historie', 70)
);
$pl = new PublicationList(new HtmlTableFactory());

$pl->displayTable($publications);


/* ****************************************************************** */
// CLI, Textova tabulka pouze pro demonstraci


class TextCell extends Cell
{
    public function show()
    {
        $diff = strlen($this->content);
        mb_strlen($this->content, 'UTF-8');
        print '|' . str_pad($this->content, 15 + $diff);
    }
}

class TextRow extends Row
{
    public function show()
    {
        foreach ($this->cells as $cell) {
            $cell->show();
        }

        print "|\n";
        $str = '';
        for ($i = 0; $i < count($this->cells); $i++) {
            $str .= '+' . str_repeat('-', 15);
        }

        print $str . "+\n";
    }
}

class TextHeader extends Header
{
    public function show()
    {
        $str = '';
        for ($i = 0; $i < count($this->cells); $i++) {
            $str .= '+' . str_repeat('-', 15);
        }

        print $str . "+\n";

        foreach ($this->cells as $cell) {
            $cell->show();
        }

        print "|\n";
        print $str . "+\n";
    }
}

class TextTable extends Table
{
    public function show()
    {
        $this->header->show();
        foreach ($this->rows as $row) {
            $row->show();
        }
    }
}

class TextTableFactory implements TableFactory
{
    public function createCell($content)
    {
        return new TextCell($content);
    }
    public function createRow()
    {
        return new TextRow();
    }
    public function createHeader()
    {
        return new TextHeader();
    }
    public function createTable()
    {
        return new TextTable();
    }
}



$publications = array(
    array('PC', 100),
    array('PC', 380),
    array('medicína', 250),
    array('historie', 70)
);

$pl = new PublicationList(new TextTableFactory());
$pl->displayTable($publications);



// ZAVEREM
/*
Návrhový vzor Abstract Factory poskytuje rozhraní pro vytvoření skupin podobných nebo souvisejících objektů bez pojmenování konkrétních tříd.
*/