Featured image of post 第4回-404エラーを検出しよう(継承の限界) - PerlとMooで学ぶDecorator

第4回-404エラーを検出しよう(継承の限界) - PerlとMooで学ぶDecorator

「404エラー検出」かつ「特定IP除外」のような複合条件により、サブクラスが爆発的に増える「クラス爆発」問題に直面します。継承の限界を体験します。

@nqounetです。

前回は「継承」を使って、IPアドレスでログをフィルタリングする機能を追加しました。

今回は、新たな要望に応える中で「継承による機能拡張の限界」に直面します。

新たな要望:404エラーも見つけたい

「特定のIPアドレスだけを見たい」と言っていた上司が、今度はこう言ってきました。

404 Not Foundのエラーが出ているログだけを抽出したいな。あ、もちろん前のIPアドレスフィルタリングと組み合わせて使うこともあるからね」

なるほど、ステータスコードが404のものを抽出する機能ですね。

継承でやってみる(悪い例)

素直に継承でやろうとすると、以下のクラスが必要になります。

  1. StatusFilteredLogParser: ステータスコードでフィルタリングするクラス
  2. IPAndStatusFilteredLogParser: IPアドレスかつステータスコードでフィルタリングするクラス

…おや?クラス名が長くなってきましたね。

コード例: IPAndStatusFilteredLogParser.pm

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package IPAndStatusFilteredLogParser;
use Moo;
extends 'IPFilteredLogParser'; # IPフィルタリング機能を継承

has target_status => ( is => 'ro', required => 1 );

sub next_log ($self) {
    # 親(IPフィルター)のnext_logを呼び出す
    while (defined(my $log = $self->SUPER::next_log)) {
        # さらにステータスコードで判定
        if ($log->{status} eq $self->target_status) {
            return $log;
        }
    }
    return undef;
}
1;

これを使えば、「特定のIP」かつ「特定のステータス」のログを抽出できます。

組み合わせ爆発(Class Explosion)

しかし、さらに要望が増えたらどうなるでしょうか?

  • 「レスポンスタイムが遅いログも抽出したい(SlowLogParser)」
  • 「特定のパスへのアクセスも抽出したい(PathLogParser)」

これらを自由に組み合わせようとすると、クラスの継承関係はどうなるでしょう?

  • IPFilteredLogParser
  • StatusFilteredLogParser
  • SlowLogParser
  • IPAndStatusFilteredLogParser
  • IPAndSlowFilteredLogParser
  • StatusAndSlowFilteredLogParser
  • IPAndStatusAndSlowFilteredLogParser

機能が1つ増えるたびに、必要なクラスの数が爆発的に増えていきます。これが「クラス爆発(Class Explosion)」と呼ばれる問題です。

さらに、「IPフィルタリングのロジックを変えたい」と思った時、IPFilteredLogParser だけでなく、それを継承している全てのクラス(IPAnd...)に影響が出るかもしれません。修正の影響範囲が読めなくなります。

解決策:コンポジション(組み合わせ)

この問題を解決するには、「継承(Is-A関係)」ではなく「コンポジション(Has-A関係)」を使う必要があります。

つまり、「IPフィルタリング機能を持つパーサー」という巨大なクラスを作るのではなく、「フィルタリングという機能(部品)を、動的にパーサーにくっつける」という発想の転換です。

これこそが、次回紹介するDecoratorパターンの考え方です。

次回予告

次回、いよいよDecoratorパターンが登場します。

継承の呪縛から解き放たれ、必要な機能をレゴブロックのように自由に組み合わせられる世界へ案内します!

第5回: Decoratorパターンで柔軟に機能追加しよう

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。