Abstract Factory Pattern

geposted am 14.03.2020, in PHP

So erstellen Sie eine Reihe verwandter oder abhängiger Objekte, ohne ihre konkreten Klassen anzugeben. Normalerweise implementieren die erstellten Klassen alle dieselbe Schnittstelle. Dem Client der abstrakten Factory ist es egal, wie diese Objekte erstellt werden, er weiß nur, wie sie zusammenpassen.

Interfaces und abstrakte Klassen

 <?php
namespace de\phpdesignpatterns\tables;

interface TableFactory {
    public function createTable();
    public function createRow();
    public function createHeader();
    public function createCell();
}


abstract class Table {
    protected $header = null;
    protected $rows = array();

    public function setHeader(Header $header) {
        $this->header = $header;
    }

    public function addRow(Row $row) {
        $this->rows[] = $row;
    }

    abstract public function display();
}

abstract class Row {
    protected $cell;
    protected $cellData = array();

/** dem Konstruktor muß hier die gemeinsam genutzte Zellinstanz übergeben werden **/
    public function __construct(Cell $cell) {
        $this->cell = $cell;
    }

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

    abstract public function display();
}

abstract class Header extends Row {
}

/** bekommt beim Aufruf keine Daten übergeben, sondern nur die display()-Methode **/
abstract class Cell {
    abstract public function display($data);
}
?>

 

TextTableFactory.php implementiert TableFactory

 <?php
namespace de\phpdesignpatterns\tables\ascii;

use de\phpdesignpatterns\tables\TableFactory;
use de\phpdesignpatterns\tables\Table;
use de\phpdesignpatterns\tables\Row;
use de\phpdesignpatterns\tables\Header;
use de\phpdesignpatterns\tables\Cell;

require_once 'TableFactory.php';

class TextTableFactory implements TableFactory {
    private $cell = null;

    public function createTable() {
        $table = new TextTable();
        return $table;
    }
    public function createRow() {
        $row = new TextRow($this->createCell());
        return $row;
    }
    public function createHeader() {
        $header = new TextHeader($this->createCell());
        return $header;
    }
    public function createCell() {
        if ($this->cell == null) {  /** es wird nur eine Instanz der Cell-Implementierung verwendet **/
            $this->cell = new TextCell(); 
        }
        return $this->cell;
    }
}

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

class TextRow extends Row {
    public function display() {
        foreach ($this->cellData as $data) {
            $this->cell->display($data);
        }
        print "|\n";
        print "+" . str_repeat("-", (count($this->cellData) * 21)-1) . "+\n";
    }
}

class TextHeader extends Header {
    public function display() {
        print "+" . str_repeat("-", (count($this->cellData) * 21)-1) . "+\n";
        foreach ($this->cellData as $data) {
            $this->cell->display($data);
        }
        print "|\n";
        print "+" . str_repeat("-", (count($this->cellData) * 21)-1) . "+\n";
    }
}

/** speichert keine Daten, sondern bekommt diese übergeben **/
class TextCell extends Cell {
    public function display($data) {
        print '|' . str_pad($data, 20);
    }
}
?>

 

2. Implementation TableFactory (HTMLTableFactory)

<?php
namespace de\phpdesignpatterns\tables\html;

use de\phpdesignpatterns\tables\TableFactory;
use de\phpdesignpatterns\tables\Table;
use de\phpdesignpatterns\tables\Row;
use de\phpdesignpatterns\tables\Header;
use de\phpdesignpatterns\tables\Cell;

require_once 'TableFactory.php';

class HtmlTableFactory implements TableFactory {
    public function createTable() {
        $table = new HtmlTable();
        return $table;
    }
    public function createRow() {
        $row = new HtmlRow();
        return $row;
    }
    public function createHeader() {
        $header = new HtmlHeader();
        return $header;
    }
    public function createCell($content) {
        $cell = new HtmlCell($content);
        return $cell;
    }
}

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

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

class HtmlHeader extends Header {
    public function display() {
        print "  <tr style=\"font-weight: bold;\">\n";
        foreach ($this->cells as $cell) {
            $cell->display();
        }
        print "  </tr>\n";
    }
}

class HtmlCell extends Cell {
    public function display() {
        print "    <td>{$this->content}</td>\n";
    }
}
?>

 

VehicleList.php

Statt für jede Zelle eine Instanz über die Fabrik zu erzeugen, übergibt man an die addCell()-Methode lediglich den Inhalt der Zelle:

 <?php
namespace de\phpdesignpatterns\tables\util;

use de\phpdesignpatterns\tables\TableFactory;

class VehicleList {

    /**
     * Table factory
     *
     * @var TableFectory
     */
    protected $tableFactory = null;

    public function __construct(TableFactory $tableFactory) {
        $this->tableFactory = $tableFactory;
    }

    public function showTable($data) {

        $table = $this->tableFactory->createTable();

        // Kopfzeile erstellen
        $header = $this->tableFactory->createHeader();
        $header->addCell('Hersteller'); /** Inhalt der Zelle statt createCell-Methode **/
        $header->addCell('Farbe');

        $table->setHeader($header);

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

 

TestText.php

 <?php
require_once 'TextTableFactory.php';
require_once 'VehicleList.php';

use de\phpdesignpatterns\tables\util\VehicleList;
use de\phpdesignpatterns\tables\ascii\TextTableFactory;

$data = array(
           array('BMW', 'blau'),
           array('Peugeot', 'rot'),
           array('VW', 'schwarz'),
        );

$list = new VehicleList(new TextTableFactory());

$list->showTable($data);
?>

 

TestHtml.php

<?php
require_once 'HtmlTableFactory.php';
require_once 'VehicleList.php';

use de\phpdesignpatterns\tables\util\VehicleList;
use de\phpdesignpatterns\tables\html\HtmlTableFactory;

$data = array(
           array('BMW', 'blau'),
           array('Peugeot', 'rot'),
           array('VW', 'schwarz'),
        );

$list = new VehicleList(new HtmlTableFactory());

$list->showTable($data);
?>

Nachteile: Bricht mit den Regeln der Kapselung. Das Objekt muß u.U. sehr viele Daten durchgereicht bekommen, da es nicht mehr alle Daten speichert, die es benötigt. Abwägung von Speichergewinn nötig.