Wzorce projektowe, cz. 5 Abstract factory

Fabryka abstrakcyjna jest wzorcem projektowym, którego zadaniem jest określenie interfejsu do tworzenia różnych obiektów należących do tego samego typu (rodziny). Interfejs ten definiuje grupę metod, za pomocą których tworzone są obiekty.


Diagram klas wzorca Abstract factory

Przykładowa implementacja

<?php

interface AbstractFactory {
    function createProduct();
}

class ConcreteFactory1 implements AbstractFactory {
    public function createProduct() {
        return new Product("Produkt 1");
    }
}

class ConcreteFactory2 implements AbstractFactory {
    public function createProduct() {
        return new Product("Produkt 2");
    }
}

interface AbstractProduct {
    function getName();
}

class Product implements AbstractProduct {
    private $name;

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

class Factory {
    const FACTORY1="Factory 1";
    const FACTORY2="Factory 2";

    public static function chooseFactory($name) {
        switch($name) {
            case self::FACTORY1:
                return new ConcreteFactory1();
                break;
            case self::FACTORY2:
                return new ConcreteFactory2();
                break;
        }
    }
}

// test
$factory1=Factory::chooseFactory("Factory 1");
$factory2=Factory::chooseFactory("Factory 2");
$product1=$factory1->createProduct();
$product2=$factory2->createProduct();

echo $product1->getName();
echo $product2->getName();

?>

Najczęściej fabrykę abstrakcyjną buduje się w postaci interfejsu. Po stronie klienta tworzone są konkretne implementacje fabryki. Konkretne obiekty tworzone są poprzez wywołanie metod interfejsu. Fabryka pozwala na tworzenie zestawów obiektów dopasowanych do konkretnych zastosowań (np. różnych funkcjonalności, platform, itp.). Każda z konkretnych fabryk realizuje odmienny zestaw klas, ale zawsze posiadają one pewien zdefiniowany zespół interfejsów.

Przykład z życia wzięty

Rozważmy fragment aplikacji odpowiedzialny za wyświetlanie treści.

<?php

// Produkty
interface Document {
    function generate();
}

class PDF implements Document {
    public function generate() {
        return 'Dokument PDF';
    }

    public function setColor($color) {
        echo "// Ustawiam kolor: ".$color;
    }

}

class HTML implements Document {
    public function generate() {
        return 'Dokument HTML';
    }

    public function set_color($color) {
        echo "// Ustawiam kolor: ".$color;
    }

}

// Fabryki
interface DocumentFactory {
    function create();
    function setColor($color);
}

class PDFFactory implements DocumentFactory {
    private $color;

    public function create() {
        $doc = new PDF();
        $doc->setColor($this->color);
        return $doc;
    }

    public function setColor($color) {
        $this->color = $color;
    }

}

class HTMLFactory implements DocumentFactory {
    private $color;

    public function create() {
        $doc = new HTML();
        $doc->set_color($this->color);
        return $doc;
    }

    public function setColor($color) {
        $this->color = $color;
    }
}

class Page {
    private $documentFactory;

    public function __construct(DocumentFactory $factory) {
        $this->documentFactory = $factory;
    }
    public function render() {
        $document = $this->documentFactory->create();
        echo $document->generate();
    }

}

// testy
$pdf = new PDFFactory();
$pdf->setColor("#000000");
$html = new HTMLFactory();
$html->setColor("#ffffff");
$page1 = new Page($pdf);
$page1->render(); // wyswietli "Dokument PDF"
$page2 = new Page($html);
$page2->render(); // wyswietli "Dokument HTML"

?>

Klasy HTML i PDF zawierają konkretne dane na temat różnych typów dokumentów (w tym wypadku są to produkty fabryki). Z kolei HTMLFactory i PDFFactory tworzą obiekty odpowiednich klas. Dzięki zastosowaniu wspólnego interfejsu dla fabryk możemy bardzo łatwo zmieniać typ generowanego dokumentu.

Zalety i wady

Zalety:

  • Odseparowanie klas konkretnych – klienci nie wiedzą jakich typów konkretnych używają, posługują się interfejsami abstrakcyjnymi.
  • Łatwa wymiana rodziny produktów.
  • Spójność produktów – w sytuacji, gdy pożądane jest, aby klasy produkty były z określonej rodziny, fabryka bardzo dobrze to zapewnia.

Wady:

  • Trudność w dołączaniu nowego produktu do rodzin produktów, spowodowana koniecznością rozszerzania interfejsów fabryki.

Zastosowanie

Wzorzec fabryki abstrakcyjnej można wykorzystać między innymi do stworzenia uniwersalnego sterownika do obsługi różnych typów baz danych (MySQL, Oracle SQL itp.).

Powiązane tematy

Print Friendly, PDF & Email