@nqounetです。
前回は通知担当をRadarLogObserverとThreatScoreObserverに分離しました。どちらもupdateメソッドを持っていて、統一的に呼び出せるようになりましたね。
でも、ここで疑問が生まれます。
「updateメソッドを持っていること、どうやって保証するの?」
今回はMoo::Roleを使って、この契約を明文化します。
Roleで契約を定義する
Moo::Roleは「このメソッドを必ず持っていなければならない」というルールを定義できます。JavaやTypeScriptでいうインターフェースのようなものです。
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
| #!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo(cpanmでインストール)
use v5.36;
package IntrusionEvent;
use Moo;
has timestamp => (
is => 'ro',
required => 1,
);
has source_ip => (
is => 'ro',
required => 1,
);
has attack_type => (
is => 'ro',
required => 1,
);
# 通知担当が満たすべき契約
package IntrusionObserver;
use Moo::Role;
requires 'update'; # updateメソッドを必須にする
package RadarLogObserver;
use Moo;
with 'IntrusionObserver'; # 契約に同意
sub update ($self, $event) {
say "=== 侵入イベント検知 ===";
say "時刻: " . $event->timestamp;
say "発信元: " . $event->source_ip;
say "攻撃種別: " . $event->attack_type;
say "";
}
package ThreatScoreObserver;
use Moo;
with 'IntrusionObserver'; # 契約に同意
my %threat_scores = (
'SSH Brute Force' => 80,
'Port Scan' => 30,
'SQL Injection Attempt' => 90,
'XSS Attack' => 70,
);
has total_score => (
is => 'rw',
default => 0,
);
sub update ($self, $event) {
my $score = $threat_scores{$event->attack_type} // 50;
$self->total_score($self->total_score + $score);
say "[脅威スコア] $score 加算 (累計: " . $self->total_score . ")";
}
package main;
my $log_observer = RadarLogObserver->new;
my $score_observer = ThreatScoreObserver->new;
my @events = (
IntrusionEvent->new(
timestamp => '2026-01-18T06:00:00+09:00',
source_ip => '192.168.1.100',
attack_type => 'SSH Brute Force',
),
);
for my $event (@events) {
$log_observer->update($event);
$score_observer->update($event);
}
|
ポイントはIntrusionObserverというRoleです。
1
2
3
4
| package IntrusionObserver;
use Moo::Role;
requires 'update'; # updateメソッドを必須にする
|
このRoleをwithで取り込んだクラスは、必ずupdateメソッドを実装しなければなりません。
契約違反を検出する
試しにupdateメソッドを実装し忘れた場合を見てみましょう。
1
2
3
4
5
| package BrokenObserver;
use Moo;
with 'IntrusionObserver'; # 契約に同意したけど...
# updateメソッドを実装し忘れた!
|
このコードを実行すると、以下のようなエラーが発生します。
1
| Can't apply IntrusionObserver to BrokenObserver - missing update
|
コンパイル時にエラーが検出されます。実行してから「あれ、動かない」ではなく、プログラムを起動した時点で問題が発覚するのです。これは大きな安心感につながります。
実行結果
正しく実装されたコードは、前回と同じように動作します。
1
2
3
4
5
6
| === 侵入イベント検知 ===
時刻: 2026-01-18T06:00:00+09:00
発信元: 192.168.1.100
攻撃種別: SSH Brute Force
[脅威スコア] 80 加算 (累計: 80)
|
見た目は変わりませんが、設計の堅牢性が向上しています。
Roleの役割
Roleは「契約書」のようなものです。
requires 'update': 「このメソッドを持っていること」という契約条項with 'IntrusionObserver': 「この契約に同意します」という署名
契約に違反すれば、すぐにエラーで教えてくれます。チームで開発するときも「このRoleを実装してね」と伝えれば、必要なメソッドが明確になります。
今回のまとめ
今回はMoo::Roleを使って、通知担当が満たすべき契約を定義しました。
IntrusionObserverロールでupdateメソッドを必須化withで契約に同意、requiresで必須メソッドを宣言- 実装漏れはコンパイル時に検出
これで「updateを持っているはず」という期待が、明文化された契約になりました。
次回予告
次回は「通知担当を一元管理する司令塔」を作ります。現在は手動で各Observerを呼び出していますが、これを自動化しましょう。
イベントが発生したら、登録されたすべてのObserverに自動で通知する仕組みを作ります。