@nqounetです。
全8回にわたってお届けしてきた「PerlとMooで学ぶDecorator」も、今回で最終回となります。
前回までの記事で、ログ読み込みからフィルタリング、統計集計、アラート通知まで、全ての機能を「部品(Decorator)」として実装しました。
最後は、これらの部品をプログラムコードを変更することなく自由に組み替えられるようにして、このシリーズを締めくくりましょう。
今回のゴール:設定駆動パイプライン
pipeline.json という設定ファイルを書き換えるだけで、ログ解析パイプラインの構成(順序やパラメータ)を自由に変更できるシステムを完成させます。
コード例1: pipeline.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| [
{
"module": "IPFilterDecorator",
"args": {
"target_ip": "127.0.0.1"
}
},
{
"module": "AlertDecorator",
"args": {
"threshold": 5
}
},
{
"module": "StatsAggregatorDecorator"
}
]
|
コード例2: PipelineBuilder.pm
JSONを読み込んでDecoratorを積み上げていくビルダークラスです。
このコードでは File::Slurper と Module::Load という2つのCPANモジュールを使用しています。事前にインストールしてください。
1
| cpanm File::Slurper Module::Load
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| package PipelineBuilder;
use Moo;
use experimental qw(signatures);
use JSON::PP qw(decode_json);
use File::Slurper qw(read_text);
use LogParser;
use Module::Load;
sub build ($self, $config_file, $log_file) {
# 1. まず基本のパーサーを作る(一番芯の部分)
my $processor = LogParser->new(filename => $log_file);
# 2. JSON設定を読み込む
my $json_text = read_text($config_file);
my $steps = decode_json($json_text);
# 3. 設定順にDecoratorを積み上げていく
for my $step (@$steps) {
my $module = $step->{module};
my $args = $step->{args} || {};
# モジュールを動的にロード(use $module と同じ)
load $module;
# Decoratorで現在のprocessorを包み込み、新しいprocessorにする
$processor = $module->new(
wrapped => $processor,
%$args,
);
}
return $processor;
}
1;
|
コード例3: main.pl(完成形)
これですべての準備が整いました。メインスクリプトは驚くほどシンプルになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| use strict;
use warnings;
use lib '.';
use PipelineBuilder;
# ビルダーを使ってパイプラインを一発構築
my $pipeline = PipelineBuilder->new->build('pipeline.json', 'access.log');
# あとは回すだけ!
while (defined(my $log = $pipeline->next_log)) {
# 処理はすべてDecoratorたちが勝手にやってくれる
}
# もし集計機能が含まれていれば、レポートを出せるかも?
if ($pipeline->can('report')) {
$pipeline->report();
}
|
開放閉鎖原則(OCP)の達成
このシステムの素晴らしい点は、新しい機能を追加したい時に、既存のコード(PipelineBuilder や他のDecorator)を一切変更する必要がないことです。
- 新しいDecoratorクラスを作る(拡張に対して開いている)
- JSONに1要素追記する
- 既存のコードは修正不要(修正に対して閉じている)
これを「開放閉鎖原則(Open/Closed Principle)」と呼び、オブジェクト指向設計の重要な目標の一つです。Decoratorパターンは、このOCPを実現するための強力な武器なのです。
まとめ
このシリーズでは、以下のステップでDecoratorパターンを学びました。
- 基本クラスを作る(LogReader, LogParser)
- 継承で機能追加しようとして「クラス爆発」に遭遇する
- Roleで共通インターフェースを定義する
- Decoratorで機能を「包み込む」構造に変える
- 状態を持たせたり(統計)、副作用を持たせたり(アラート)する
- 設定ファイルから動的に構築する
みなさんの開発現場でも、「ちょっと機能を追加したいけど、継承だと影響範囲が怖いな…」と思った時は、ぜひDecoratorパターンを思い出してください。
きっと、あなたのコードを柔軟で強固なものにしてくれるはずです。
長い連載にお付き合いいただき、ありがとうございました!