PHP

PHPで使うGoFパターン ひとり Advent Calendar - CoR

More than 5 years have passed since last update.


CoRって?

いままでカタカナ表記を頑に守ってきたのですが、こいつは名前が長いので略式表記にします。"Chain of Responsibility"です。イメージはDNSですかね。

はじめのDNS「jp? ああ、あいつに聞くか」

JPのDNS「co?ああ、あいつだ」

coのDNS「aainc?ああ、ああ、あいつだ」

aaincのDNS「ブログ? ああ、それならあそこだ」

と、いう感じでIPアドレスに解決します。これをオブジェクト指向でやるのがCoRです。


CoRの構造


  • Client - 使う人

  • IHandler - 上記の例で言えばDNSサーバーのインターフェース。単純なので今回は抽象クラスにします。

  • 実Handler - 上記の例で言えば実際のレベルごとのDNSです

<?php

function say ( $l ) { print "$l\n"; }

abstract class HandlerBase {
private $_Next = array ();
private $_Dictionary = array ();
public function query ( $query ) {
if ( !$query ) throw new Exception ( 'クエリが無いっす' );
$elms = preg_split ( '#\.#', $query );
$elm = array_pop ( $elms );
if ( !$elms ) return $this->_Dictionary[$elm];
else return $this->getNext($elm)->query ( join ( '.' , $elms ) );
}

public function setNext ( $key, $obj ) {
if ( !( $obj instanceof HandlerBase ) )
throw new Exception ( 'そんなオブジェクト無理だもん' );
$this->_Next[$key] = $obj;
return $this;
}

public function getNext ( $elm ) {
if ( $this->_Next[$elm] ) return $this->_Next[$elm];
else throw new Exception ( 'Nextないっす' );
}

public function setDictionary ( $dict ) {
$this->_Dictionary = $dict;
return $this;
}
}

class DNS extends HandlerBase {
}

$hoge = new DNS ();
$hoge->setDictionary ( array (
'wiki' => '1.1.1.2',
'blog' => '1.1.1.3',
));

$fuga = new DNS ();
$fuga->setDictionary ( array (
'wiki' => '2.1.1.2',
'blog' => '2.1.1.3',
));

$com = new DNS ();
$com->setDictionary ( array (
'hoge' => '1.1.1.1',
'fuga' => '2.1.1.1',
))
->setNext ( 'hoge', $hoge )
->setNext ( 'fuga', $fuga );

$tld = new DNS ();
$tld->setNext ( 'com', $com );

say ( $tld->query ( 'hoge.com' ) == '1.1.1.1' ? 'OK' : 'NG' );
say ( $tld->query ( 'wiki.hoge.com' ) == '1.1.1.2' ? 'OK' : 'NG' );
say ( $tld->query ( 'blog.fuga.com' ) == '2.1.1.3' ? 'OK' : 'NG' );
say ( $tld->query ( 'fuga.com' ) == '2.1.1.1' ? 'OK' : 'NG' );

とりあえず今回は単純なので具象クラスは一個にしちゃいましたが中間のレベルの具象クラスを目的に特化させて派生したものをsetNextする設計でも良いです。


どういう時に使うの?

身も蓋もない言い方をすると「そういうデータ構造を表す場合に使う」ということになります。

具体的には以下。


  • MVCフレームワークやAPIでのURLのルーティング

  • ツリー構造の検索

  • ファイルシステムのラッパ

どちらかというと業務からは遠いですね。(勿論、そういうデータ構造を扱うならば使うでしょう)

ライブラリの設計者などがよく使うかも知れません。


端書き

このパターンはインタンスの生成が複雑になり易いです。FactoryMethodやAbstractFactoryを併用しましょう。