前回の
動的なモジュールロードで実現できること
動的なモジュールロードをすると実現できることを紹介します。
コード量が少なくなる
ロードするモジュールの名前を動的に生成できるため、use
でモジュールロードする場合と比べて書く必要のあるコード量が少なくなります。
たとえば次のように、
use Module::Load qw( load );
for my $module_name (qw/ Foo Bar /) {
load('MyApp::' . $module_name);
}
動的なディスパッチ
コード量が少なくなることの中でも特筆すべき点として、
項目の多いマスタデータや、
具体的な利用例として、
データベースにある大量のアイテムデータと、
use Class::Load qw( load_class );
use String::CamelCase qw( camelize );
sub use {
my ($self, $user) = @_;
my $module =
'MyApp::ItemEffect::' . camelize($self->name);
load_class($module);
my $effect = $module->new(@_);
$effect->execute($user);
}
起動/ロード時間の短縮
モジュールロードするタイミングを、Moo
、Type::Tiny、
など、
依存関係の動的な解決
依存関係を動的に解決できるので、
動的なモジュールロードの活用
これらの動的なモジュールロードで実現できることが、
コード量を少なくする
コード量を少なくするために活用されている事例を見てみましょう。
Module::Findの事例──特定の名前空間に属するモジュールをすべて読み込む
Module::Findは、usesub
で引数の名前空間直下に属するモジュールをロードし、useall
で引数の名前空間に属するモジュールを再帰的にロードします。
use Module::Find qw( usesub useall );
# Foo::Bar, Foo::Baz, ...
my @sub_modules = usesub('Foo');
# Foo::Bar, Foo::Bar::Hoge, Foo::Baz, etc...
my @all_modules = useall('Foo');
Catalystのコンテナ──use忘れを防ぎ、モジュール名を短縮する
CatalystというPerl製のWAFCatalyst
ではアプリケーションの起動時にすべてのモデルモジュールをロードし、use
忘れがなくなる、
実際にCatalyst
で作られたアプリケーションでどのようにモデルモジュールが使われるのかを見てみましょう。
package MyApp::Controller::Root;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller' }
__PACKAGE__->config(namespace => '');
sub foo :Local :Args(1) {
my ($self, $c, $name) = @_;
my $country =
$c->model('DB::Countries') ―(1)
->search({ name => $name }); ―(2)
$c->stash(
template => 'foo.tt',
country => $country,
);
}
このコードはCatalyst
のコントローラのコードになります。
前述したとおり、
MyApp::Model::DB::Countries
のインスタンスを取得しています。
search
メソッドを呼び出しています。
Catalystのコンテナ機能と似た事例にモデルモジュール専用のモジュールローダがあり、Catalyst
とは別のWAFのプラグインなどで見受けられます。これは起動時にすべてのモデルモジュールのロードをするのではなく、
プラガブルなアーキテクチャの実装──Tengの事例
簡単にプラグインの実装と追加ができるようになるため、
例としてTengのプラグイン機構を簡略化したコードでプラガブルなアーキテクチャの実装方法を紹介します。
Teng
は軽量なO/Teng::Plugin::Count
を使うと、
プラグイン機構の実装
Teng
のようなプラグイン機構は、@EXPORT
で指定されたメソッド)
package Module;
use Module::Load qw( load );
sub add_plugin {
my ($class, $plugin) = @_;
my $package = "Module::Plugin::${plugin}";
load($package);
{
no strict 'refs';
for my $method ( @{"${package}::EXPORT"} ){
*{$class . '::' . $method} =
$package->can($method);
}
}
}
プラグインモジュールの実装
次に、Module::Plugin::${プラグイン名}
といった風に、@EXPORT
に追加します。
package Module::Plugin::Hoge;
our @EXPORT = qw( do_something );
sub do_something {
my $class = shift;
warn 'Do something';
}
プラグインモジュールの利用
プラグインモジュールを利用するには、add_
メソッドにプラグイン名を渡します。
モジュール本体で直接プラグインを利用すると、
package My::Module;
use parent 'Module';
__PACKAGE__->add_plugin('Hoge');
プラグインを利用したならば、
use My::Module;
My::Module->do_something();
フレームワークの実装──Mojoliciousのルーティングの事例
動的なモジュールロードを活用すると、
例として、
MojoliciousはPerl製のWAFで、
package SomeWebApp;
use Mojo::Base 'Mojolicious';
# アプリケーション起動時に呼ばれるメソッド
sub startup {
my $self = shift;
my $r = $self->routes;
$r->get('/')->to('Root#root'); ―(1)
my $user =
$r->any('/user')->to(controller => 'user'); ―(2)
$user->post('/add/:name')->to(action => 'add'); ―(3)
}
SomeWebApp::Controller::Root
クラスのroot
メソッドを呼び出す指定をしています。
/user
にあらゆるメソッドでリクエストが来た際SomeWebApp::Controller::User
クラスのメソッドで処理することを指定しています。$user_
にはその状態を保持したルーティングオブジェクトが格納されているので、SomeWebApp::Controller::User
クラスのどのメソッドで処理を行うかの指定をするだけで簡潔にリクエストに応じた呼び出す処理を割り当てることができます。
/user/
SomeWebApp::Controller::User
クラスのメソッドを指定しています。
このように動的なモジュールロードを活用すると、
<続きの
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/
定価1,628円
ISBN978-4-297-13000-8
- 特集1
イミュータブルデータモデルで始める
実践データモデリング
業務の複雑さをシンプルに表現! - 特集2
いまはじめるFlutter
iOS/Android両対応アプリを開発してみよう - 特集3
作って学ぶWeb3
ブロックチェーン、スマートコントラクト、 NFT