Builder を自作する方法
概要
新しい Builder クラスを自作するには Builder と Context のそれぞれのサブクラスを作る必要があります.
Context とはノードを変換する際に使われる中間生成物を表すクラスです. 以下の機能を持ちます.
- 各種 handle メソッド: 各ノードを文字列などに変換するためのロジックを定義します.
- getResult(): ノードの変換結果を取り出すメソッドです. このメソッドの返り値がそのまま Peach_Markup_Builder::build() の結果として返されます.
引数の Component のクラス名をそのまま返すだけの簡単な Builder を作るチュートリアルを以下に掲載します.
新しい Context クラスの作成
まずは Context クラスを継承して独自の Context を定義します. Context クラスで定義されている抽象メソッドすべてを実装する必要があります.
- {
- private $result = "";
- public function getResult()
- {
- return $this->result;
- }
- public function handleCode(Peach_Markup_Code $node)
- {
- }
- public function handleComment(Peach_Markup_Comment $node)
- {
- }
- public function handleContainerElement(Peach_Markup_ContainerElement $node)
- {
- }
- public function handleEmptyElement(Peach_Markup_EmptyElement $node)
- {
- }
- public function handleNodeList(Peach_Markup_NodeList $node)
- {
- }
- public function handleNone(Peach_Markup_None $none)
- {
- }
- public function handleText(Peach_Markup_Text $node)
- {
- }
- }
新しい Builder クラスの作成
次に先ほど作成した MyContext に処理を渡すための新しい Builder クラスを作成します. 以下のように createContext() メソッドの中で新しい MyContext オブジェクトを生成して返すようにします.
- {
- protected function createContext()
- {
- return new MyContext();
- }
- }
それではこの MyBuilder クラスを使ってノードを変換してみましょう. 以下のような結果が得られます.
- $builder = new MyBuilder();
- /*
- Output:
- Peach_Markup_Text
- Peach_Markup_ContainerElement
- Peach_Markup_EmptyElement
- */
再帰的にノードを処理する方法
先ほど作った MyContext は, 内部に子ノードを含むノード (Container と呼びます) を再帰的に処理することが出来ません. 子ノードを再帰的に処理するには以下のようにしてください.
- getChildNodes() で子ノードの一覧を取得します
- 取得した各子ノードについて handle() メソッドを適用します
handle() は引数のノードの種類に応じて handleText() や handleContainerElement() など適切なメソッドに処理を割り振るメソッドです. (Visitor パターンにおける visit() メソッドに相当します)
以下にサンプルコードを掲載します.
- {
- private $result;
- private $indent;
- public function __construct()
- {
- $this->result = "";
- }
- public function getResult()
- {
- return $this->result;
- }
- private function handleCommon(Peach_Markup_Component $c)
- {
- $this->indent->stepUp();
- $childNodes = $c->getChildNodes();
- $this->indent->stepDown();
- }}
- public function handleCode(Peach_Markup_Code $node)
- {
- $this->handleCommon($node);
- }
- public function handleComment(Peach_Markup_Comment $node)
- {
- $this->handleCommon($node);
- }
- public function handleContainerElement(Peach_Markup_ContainerElement $node)
- {
- $this->handleCommon($node);
- }
- public function handleEmptyElement(Peach_Markup_EmptyElement $node)
- {
- $this->handleCommon($node);
- }
- public function handleNodeList(Peach_Markup_NodeList $node)
- {
- $this->handleCommon($node);
- }
- public function handleNone(Peach_Markup_None $none)
- {
- $this->handleCommon($none);
- }
- public function handleText(Peach_Markup_Text $node)
- {
- $this->handleCommon($node);
- }
- }
- {
- protected function createContext()
- {
- return new MyContext2();
- }
- }
MyBuilder2 を実際に使用したサンプルコードを以下に掲載します. はじめに作成した MyBuilder とは違い, 子ノードについても再帰的に処理出来ていることが確認できます.
- $root->append("Text 1");
- $root->append("Text 2");
- $sub->append("Text 3");
- $sub->append("Text 4");
- $root->append($sub);
- $root->append("Text 5");
- echo "Test: MyBuilder" . PHP_EOL;
- $b1 = new MyBuilder();
- echo $b1->build($root) . PHP_EOL;
- echo "Test: MyBuilder2" . PHP_EOL;
- $b2 = new MyBuilder2();
- echo $b2->build($root) . PHP_EOL;
- /*
- Output:
- Test: MyBuilder
- Peach_Markup_ContainerElement
- Test: MyBuilder2
- Peach_Markup_ContainerElement
- Peach_Markup_Text
- Peach_Markup_Text
- Peach_Markup_ContainerElement
- Peach_Markup_Text
- Peach_Markup_Text
- Peach_Markup_Text
- */
- Prev: Html (HTML 出力専用クラス)
- Next: 汎用モジュール群 Util