第9章


Abstract Factoryパターン(関連する部品をくみ上げて製品を作る)

「Abstract Factory」とは?

インターフェイスだけを使って、抽象的な部品で抽象的な製品を作成する。
これをAbstract Factoryパターンという

Abstract Factoryの具体例 (サンプルプログラム)

以下のようなファイル構成になっている。

  • Main.class.php
      factory
        Factory.class.php
        Item.class.php
        Item.class.php
        Link.class.php
        Page.class.php
        Tray.class.php
     listfactory
        ListFactory?.class.php
        ListItem?.class.php
        ListLink?.class.php
        ListPage?.class.php
        ListTray?.class.php

抽象的な部品

抽象的な部品:Itemクラス

<?php

abstract class Item {

    protected $caption;

    public function __construct($caption) {
        $this->caption = $caption;
    }
    public abstract function makeHTML();
}

LinkとTrayのスーパークラスになっている。
captionフィールドはこの項目の「見出し」を表す。


抽象的な部品:Linkクラス

<?php

require_once("Item.class.php");

abstract class Link extends Item {

    protected $url;

    public function __construct($caption, $url) {
        parent::__construct($caption);
        $this->url = $url;
    }
}

HTMLのハイパーリンクを抽象的に表現したクラス。
なお、スーパークラスの抽象メソッドは実装していない。

抽象的な部品:Trayクラス

<?php

require_once("Item.class.php");

abstract class Tray extends Item {

    protected $tray;

    public function __construct($caption) {
        parent::__construct($caption);
        $this->tray = array();
    }

    public function add($item) {
        $this->tray[] = $item;
    }
}

複数のTrayやLinkを集めて一まとめにするクラス。
addメソッドを使って集める。
スーパークラスの抽象メソッドは実装していない。

抽象的な部品:Pageクラス

<?php

abstract class Page {

    protected $title;
    protected $author;
    protected $content;

    public function __construct($title, $author) {

        $this->title = $title;
        $this->author = $author;
        $this->content = array();

    }

    public function add($item) {
        $this->content[] = $item;
    }

    public function output() {
        try { 
            $filename = $this->title."html";
            $this->makeHTML();
            echo $filename."を作成しました。";
        } catch (Exception $e) {
            echo $e;
            exit;
        }
    }
    public abstract function makeHTML();
}

HTML全体を抽象的に表現したクラス。
outputメソッドでタイトルを元にファイル名を決定し、
makeHTMLメソッドを使って自分自身のHTMLの内容を書き込んでいる。

抽象的な工場:Factoryクラス

<?php

require_once("listfactory/ListFactory.class.php");

abstract class Factory {

    public static function getFactory($classname) {

        $factory = null;

        try {
            $factory = new $classname;
        } catch (Exception $e) {
            echo $e;
            exit;
        }
        return $factory;
    }

    public abstract function createLink($caption, $url);
    public abstract function createTray($caption);
    public abstract function createPage($title, $author);
}

作成する具体的な工場のクラス名を引数にして、そのクラスのインスタンスを作る。
定義された抽象メソッドは、この抽象的な工場で部品や製品を作成するときに用いるもいの。
実際の具体的な作成はサブクラスに任せている。

Mainクラス

<?php

require_once("factory/Factory.class.php");

class Main {

    public function __construct($classname) {

        $factory = Factory::getFactory($classname);

        $asahi = $factory->createLink("朝日新聞", "http://www.asahi.com/");
        $yomiuri = $factory->createLink("読売新聞", "http://www.yomiuri.co.jp/");
        $yahoo = $factory->createLink("Yahoo!", "http://www.yahoo.co.jp/");
        $google = $factory->createLink("Google", "http://www.yahoo.co.jp/");

        $trynews = $factory->createTray("新聞");
        $trynews->add($asahi);
        $trynews->add($yomiuri);

        $trysearch = $factory->createTray("サーチエンジン");
        $trysearch->add($yahoo);
        $trysearch->add($google);

        $page = $factory->createPage("LinkPage", "當銘大河");
        $page->add($trynews);
        $page->add($trysearch);
        $page->output();
    }
}

new Main("ListFactory");

実行クラス。
ここでは抽象クラスのFactoryがあるだけで、
具体的な部品、製品、工場が一切出てきていない


具体的な部品

具体的な工場:ListFactory?クラス

<?php

require_once("listfactory/ListLink.class.php");
require_once("listfactory/ListTray.class.php");
require_once("listfactory/ListPage.class.php");

class ListFactory extends Factory {

    public function createLink($caption, $url) {
        return new ListLink($caption, $url);
    }

    public function createTray($caption) {
        return new ListTray($caption);
    }

    public function createPage($title, $author) {
        return new ListPage($title, $author);
    }
}

具体的な部品:ListLink?クラス

具体的な部品:LinkTray?クラス

具体的な部品:LinkPage?クラス


Abstruct Factoryパターンにでてくるもの

AbstructProduct?(抽象的な製品)

AbstructFactory?役によって作り出される抽象的な部分や製品のインターフェイスを定める。
サンプルプログラムではLinkクラス、Trayクラス、Pageクラスがこの役をつためる

AbstructFactory?(抽象的な工場)

AbstructProduct?役のインスタンスを作り出すためのインターフェイスを定める。
サンプルプログラムではFactoryクラスがこの役をつとめる。

Client(依頼者)

AbstructProduct?役とAbstructFactory?役のインターフェイスだけを使って仕事を行う。
この仕事を行うクラスでは、具体的な部品や製品や工場については知らない。

ConcreteProduct?(具体的な製品)

AbstructProduct?役のインターフェイスを実装する。


ConcreteFactory?(具体的な工場)

AbstructFactory?役のインターフェイスを実装する。

Abstruct Factoryパターンの利点

具体的な工場を追加するのは簡単。

どのようなクラスを作り、どのようなメソッドを作ればいいのかがわかっているから。
逆に不便な点は、新たに部品を追加するのが困難であるとうこと。

関連しているパターン

Builderパターン

Abstruct Factoryパターンはインターフェイスが定まっている抽象的な部品を組み合わせて、
複雑なインスタンスを作る。
Builderパターンは段階を追って大きなインスタンスを作る。


参考資料

  • Java言語で学ぶデザインパターン入門