NEXT.pm は EVERY が魅力的

今更ながら NEXT で遊んでます。イニシャライザやデストラクタでいちいち $self->NEXT::method() と書くのも苦痛ですしミスも発生しそうだなぁと思って POD を読んでいたら,EVERY や EVERY::LAST という修飾子もあると。これらは Perl 5 で標準の「depth-first, left-to-right」な探索じゃなくて「breadth-first-dependency-wise」な探索をします。正直なんのことやらよくわからないんですが,依存関係からわりと最適な順序を導出するみたいで,呼び出され方を見ると意外に直感的でした(EVERY のほうだと大元の親クラスが最後に呼び出され,EVERY::LAST だと親クラスが最初に呼び出される感じ)。

ともかく EVERY の何がいいって,呼び出すメソッド自体でいちいち $self->NEXT::method と書かなくてよいところ。ただ,呼び出し元が 'method' じゃまずいんで

  • メソッド foobar を実装するのは一度のみ(ここで EVERY::do_foobar する)
  • そのかわり do_foobar は各所で実装してよい(その中で NEXT 系を使う必要はない)

みたいなルールを制定すれば,依存関係のあるプラグインアーキテクチャをわりかし楽にインプリメントできるのではないかと思いました。

たとえば,

#!/usr/bin/perl

use strict;
use NEXT;

package Plugin;

sub finalyze {
  my $self = shift;
  print __PACKAGE__, ": finalyze begin\n";
  $self->EVERY::do_finalyze;
  print __PACKAGE__, ": finalyze end\n";
}

package Plugin::Response;
use base qw(Plugin);

sub do_finalyze {
  my $self = shift;
  $self->finalyze_header;
}

sub finalyze_header {
  my $self = shift;
  print __PACKAGE__, ": finalyze_header begin\n";
  $self->EVERY::do_finalyze_header;
  print __PACKAGE__, ": finalyze_header end\n";
}

package Plugin::Cookie;
use base qw(Plugin Plugin::Response);

sub do_finalyze_header {
  my $self = shift;
  $self->finalyze_cookie;
}

sub finalyze_cookie {
  my $self = shift;
  print __PACKAGE__, ": finalyze_cookie begin\n";
  $self->EVERY::do_finalyze_cookie;
  print __PACKAGE__, ": finalyze_cookie end\n";
}

package Plugin::UserInfo;
use base qw(Plugin Plugin::Cookie);

sub do_finalyze_cookie {
  my $self = shift;
  print __PACKAGE__, ": store user_info into cookie\n";
}

package Plugin::Session;
use base qw(Plugin Plugin::Cookie);

sub do_finalyze_cookie {
  my $self = shift;
  $self->finalyze_session;
}

sub finalyze_session {
  my $self = shift;
  print __PACKAGE__, ": finalyze_session begin\n";
  $self->EVERY::do_finalyze_session;
  print __PACKAGE__, ": finalyze_session end\n";
}

package App;
use base qw(Plugin::Session Plugin::UserInfo);

package main;

my $app = bless {}, 'App';
$app->finalyze;

の出力は,

Plugin: finalyze begin
Plugin::Response: finalyze_header begin
Plugin::Cookie: finalyze_cookie begin
Plugin::Session: finalyze_session begin
Plugin::Session: finalyze_session end
Plugin::UserInfo: store user_info into cookie
Plugin::Cookie: finalyze_cookie end
Plugin::Response: finalyze_header end
Plugin: finalyze end

になります。なんだかよくわからないサンプルですいません。