post_ico4

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

Co sądzisz o wpisie?
BeżnadziejnySłabyŚredniDobryBardzo dobry (3 głosów, średnia ocen: 3,00 z 5)
Loading...
  • Wujek dobra rada

    „Concrete” tłumaczy się też jako specyficzny.

  • Doris

    dopiero uczę się wzorców i nie rozumiem do końca po co tworzyć do każdej klasy Document (PDF, HTML) odpowiednie fabryki (HTMLFactory, PDFFactory).
    czy to nie jest niepotrzebna dodatkowa praca?
    czy nie można klasy Page zapisac po prostu:
    class Page {
    private $document;

    public function __construct(Document $doc) {
    $this->document = $doc;
    }
    public function render() {
    echo $this->document->generate();
    }

    }
    a potem:
    $page1 = new Page(new PDF);
    $page1->render(); // wyswietli „Dokument PDF”

  • Żeby wygenerować dokument PDF trzeba zazwyczaj skonfigurować bibliotekę – podać krój czcionki, kolor, rozmiar itp. Klasę PDFFactory możemy właśnie wykorzystać do „zautomatyzowania” tych operacji – z punktu widzenia użytkownika generowanie dokumentu html, jak i pdf będzie wyglądało tak samo ;) Jakbyś przekazywał do Page bezpośrednio produkt (HTML lub PDF) użytkownik sam za każdym razem musiałby konfigurować obiekty.

    Mam nadzieję, że trochę rozjaśniłem sens stosowania fabryki.

  • Doris

    chyba nie do końca rozumiem…
    próbuję to sobie przełożyć na jakiś konkretny przykład.
    Przypuśćmy mamy stronę z prostym formularzem do generowania dokumentu. Użytkownik może wybrać sobie czy chce generować dokument w pdf czy html, może wybrać krój, i kolor czcionki.
    Wysyła formularz i teraz po stronie php
    (upraszczając) możemy napisać:
    if ($_POST[‚pdf’]){
    $factory = new PDFFactory()
    } else {
    $factory = new HTMLFactory()
    }
    $factory->setColor(‚#fff’); // musimy miec te metode dopisana w interf DocumentFactory
    $page1 = new Page($factory);//tutaj musimy przekazywac parametry ktore ustawialismy, jolor, czcionka itd
    $page1->render();

    ale przecięż równie dobrze możemy zapisać:

    if ($_POST[‚pdf’]){
    $doc= new PDF()
    } else {
    $doc= new HTML()
    }
    $doc->setColor(‚#fff’);
    $page1 = new Page($doc);
    $page1->render();

    mam wrażenie, że ciągle czegoś tu nie rozumiem.
    Albo inaczej: rozumiem o co chodzi w samym tym wzorcu i jego mechanizmie ale nie bardzo wiem gdzie go konkretnie użyć i w jaki sposób :/

  • Tylko zauważ, że do generowania plików HTML czy PDF użyjesz różnych bibliotek, a zatem różnych API (inne nazwy metod itp). Dzięki zastosowaniu fabryki możesz ujednolicić ich interfejs dla użytkownika. Nie będzie musiał zastanawiać się jakie metody ma dostępne przy PDF a jakie przy HTML – ty mu zapewnisz jednakowy interfejs do obu ;)

    Prawda, że wygodniej poznać jeden interfejs, a resztą zajmie się sam skrypt? :) I w moim przykładzie masz tylko 2 typy produktów, a może być ich znacznie więcej..

    EDIT: Zmodyfikowałem trochę przykład. Zwróć uwagę na metodę create() w obu fabrykach.

  • Doris

    w takim przypadku rzeczywiście lepiej mieć jeden interface i jedną metodę np setColor dla wszystki dokumentów. zaczynam rozumieć :)
    Dzięki za pomoc!
    Piszę właśnie swój pierwszy sklep w php/mysql.
    Właśnie zastanawiam się gdzie mogę wykorzystać ten wzorzec…

  • Na siłę nie szukaj zastosowań. Jeżeli rozumiesz idee wzorca zastosowanie znajdziesz w trakcie pisania aplikacji :) One istnieją, by ułatwiać niektóre rzeczy, ale nie powinny być „sztuką dla sztuki”.

  • Doris

    ok
    teraz zabieram się za Factory method
    zastanawiam się czym tym wzorzec różni się od Abstract factory.
    uff..

  • Te wzorce są bardzo podobne i mogą być używane wymiennie w wielu sytuacjach. W podsumowaniu cyklu postaram się opisać kiedy lepiej użyć jeden z nich :)