LoginSignup
4
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2012-08-15

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を併用しましょう。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4