読者です 読者をやめる 読者になる 読者になる

Chain Of Responsibilityパターン

同じインタフェースを持つHandlerオブジェクトを鎖(Chain)のように繋いでおき、各Handlerオブジェクトは要求された命令を処理できなければ次のオブジェクトに処理を回すパターン。Handlerに渡される命令にはCommandパターンが使われることがある。

メリット
  1. 利用する側はどのオブジェクトが要求を処理できるか知る必要がない(=疎結合
  2. 動的に連鎖を処理するHandlerを追加できる
使用例
  • HTTP Request Handler
簡単な実装

決して処理しないHandler(Never), 1/2の割合で処理に成功するハンドラ(Random), 必ず処理するハンドラ(Always)を実装してみる

  • Handlerクラス
package Handler;
use strict;
use Carp;

sub new {
    my $class = shift;
    return bless {}, $class;
}

sub next {
    my ($self, $next_handler) = @_;
    $self->{_next} = $next_handler;
    return $next_handler;
}

sub handle {
    my ($self, $request) = @_;

    my $ret = $self->_handle_impl($request);
    if ($ret ne '') {
        return $ret;
    }
    elsif (my $next = $self->{_next}) {
        return $next->handle($request);
    }
    else {
        die "fail.\n";
    }
}

sub _handle_impl {
    my ($self, $request) = @_;
    croak 'this method must be extended.';
}
1;
  • Handler::Neverクラス
package Handler::Never;
use strict;
use base qw(Handler);

sub _handle_impl {
    my ($self, $request) = @_;
    return '';
}
1;
  • Handler::Randomクラス
package Handler::Random;
use strict;
use base qw(Handler);

sub _handle_impl {
    my ($self, $request) = @_;

    if ($request eq 'random') {
        my $rand = int(rand(2));
        return 'handle at random' if ($rand == 1);
    }
    else {
        return '';
    }
}
1;
  • Handler::Alwaysクラス
package Handler::Always;
use strict;
use base qw(Handler);

sub _handle_impl {
    my ($self, $request) = @_;
    return 'handle at always';
}
1;
  • mainプログラム
use Handler;
use Handler::Never;
use Handler::Random;
use Handler::Always;

# init
my ($hdl_n, $hdl_r, $hdl_a) = (
    new Handler::Never(),
    new Handler::Random(),
    new Handler::Always(),
);

# make chain
$hdl_r->next($hdl_n)->next($hdl_a);

# test
my $ret;
$ret = $hdl_r->handle('test');
print "command test: $ret\n";


$ret = $hdl_r->handle('random');
print "command random: $ret\n";
  • 結果
kotaro@~$ perl test.pl
command test: handle at always
command random: handle at always

kotaro@~$ perl test.pl
command test: handle at always
command random: handle at random
まとめ
  • Chain Of Responsibilityパターンを使うと、コマンドを処理するクラスと利用クラスを疎結合にできる。
  • Commandパターンと相性がよい。