Featured image of post 第4回-API統合の設計課題 - Adapterパターンの限界

第4回-API統合の設計課題 - Adapterパターンの限界

Adapterパターンだけでは解決できない「呼び出し側の複雑さ」を整理。Facadeパターン導入の必要性と、API統合における新たな設計課題を解説します。

前回の振り返り

前回は、Adapterパターンを使って各APIのインターフェースを統一しました。どのAdapterも同じget_weatherメソッドで天気情報を取得できるようになりました。

しかし、まだ問題が残っています。今回は、その問題を明らかにします。

今回の目標

第4回となる今回は、Adapterパターンだけでは解決できない課題を整理します。なぜ「もう一つのパターン」が必要なのかを理解することが目標です。

呼び出し側のコードを見てみる

前回の完成コードでは、呼び出し側(メイン処理)で以下のことをしていました。

1
2
3
4
5
6
7
# 各Adapterを作成
my $owm = WeatherAdapter::OpenWeatherMap->new;
my $ws  = WeatherAdapter::WeatherStack->new;

# それぞれを個別に呼び出し
$owm->show_weather('Tokyo');
$ws->show_weather('Tokyo');

このコードには、以下の問題があります。

問題1: Adapterの管理が呼び出し側の責任

利用者は「どのAdapterがあるか」「どのように作成するか」を知っている必要があります。新しいAPIが追加されたら、呼び出し側のコードも更新が必要です。

問題2: フォールバック処理の実装が必要

APIは時々失敗します。「OpenWeatherMapが失敗したらWeatherStackを試す」というフォールバック処理を、呼び出し側で実装する必要があります。

1
2
3
4
5
6
7
8
my $weather = $owm->get_weather('Tokyo');
if (!$weather) {
    $weather = $ws->get_weather('Tokyo');
}
if (!$weather) {
    $weather = $wa->get_weather('Tokyo');
}
# まだまだ続く...

問題3: 共通処理の重複

複数の場所でAdapterを使う場合、毎回同じような初期化処理とフォールバック処理を書くことになります。

実際の利用シーンを想像する

天気予報アグリゲーターを実際のアプリケーションで使う場面を想像してみましょう。

ケース1: Webアプリケーション

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# コントローラー
sub weather_action ($self, $city) {
    my $owm = WeatherAdapter::OpenWeatherMap->new;
    my $ws  = WeatherAdapter::WeatherStack->new;
    
    my $weather = $owm->get_weather($city);
    $weather //= $ws->get_weather($city);
    
    return { weather => $weather };
}

ケース2: CLIツール

1
2
3
4
5
6
7
8
# CLIツール
my $owm = WeatherAdapter::OpenWeatherMap->new;
my $ws  = WeatherAdapter::WeatherStack->new;

my $weather = $owm->get_weather($city);
$weather //= $ws->get_weather($city);

say format_weather($weather);

ケース3: バッチ処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# バッチ処理
for my $city (@cities) {
    my $owm = WeatherAdapter::OpenWeatherMap->new;
    my $ws  = WeatherAdapter::WeatherStack->new;
    
    my $weather = $owm->get_weather($city);
    $weather //= $ws->get_weather($city);
    
    save_to_database($weather);
}

同じようなコードが3箇所に散らばっています。これはDRY原則(Don’t Repeat Yourself)に違反しています。

理想的な呼び出し方

利用者が本当に欲しいのは、こんなシンプルなインターフェースです。

1
2
3
# 理想的な呼び出し方
my $facade = WeatherFacade->new;
my $weather = $facade->get_weather('Tokyo');

利用者が気にすべきでないこと:

  • どのAPIサービスを使っているか
  • APIが何種類あるか
  • どの順番で試すか
  • 失敗時にどうフォールバックするか

これらはすべて「WeatherFacade」の内部で処理されるべきです。

Facadeパターンの予告

この「複雑な内部処理を隠蔽し、シンプルなインターフェースを提供する」パターンを「Facadeパターン」と呼びます。

Facadeは「建物の正面(ファサード)」を意味します。建物の裏側がどんなに複雑でも、正面から見れば美しい一枚の壁に見える。それと同じです。

Facadeの責務

WeatherFacadeは以下の責務を持ちます。

  • 複数のAdapterを管理する
  • 適切なAdapterを選択する
  • 失敗時に次のAdapterを試す(フォールバック)
  • 呼び出し側にはシンプルなインターフェースを提供する

クラス構成のイメージ

1
2
3
4
5
6
7
呼び出し側
    ↓ get_weather('Tokyo')
WeatherFacade
    ↓ 内部で管理
    ├── WeatherAdapter::OpenWeatherMap
    ├── WeatherAdapter::WeatherStack
    └── WeatherAdapter::WeatherAPI

現状のコードの問題点まとめ

現状のAdapterパターンだけの実装には、以下の問題があります。

問題説明
Adapter管理の責任呼び出し側がAdapterを知っている必要がある
フォールバック処理呼び出し側で実装が必要
コードの重複複数箇所で同じ処理を書く
変更の影響範囲API追加時に複数箇所の修正が必要

今回の完成コード

今回は新しいコードを書くのではなく、問題を整理しました。前回のコードに「こんな使い方をしたい」という呼び出し側のコードを追加したものを示します。

 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
#!/usr/bin/env perl
use v5.36;

# (前回のAdapter定義は省略)

# メイン処理:現状の問題点を示す
package main {
    use v5.36;

    say "=== 現状の問題点 ===";
    say "";

    # 問題1: Adapterの管理が呼び出し側の責任
    my $owm = WeatherAdapter::OpenWeatherMap->new;
    my $ws  = WeatherAdapter::WeatherStack->new;

    # 問題2: フォールバック処理が呼び出し側に必要
    my $weather = $owm->get_weather('Tokyo');
    if (!$weather) {
        $weather = $ws->get_weather('Tokyo');
    }

    if ($weather) {
        say "Tokyo: $weather->{condition}(気温 $weather->{temperature}℃)";
    }

    say "";
    say "--- 理想的な呼び出し方 ---";
    say '# my $facade = WeatherFacade->new;';
    say '# my $weather = $facade->get_weather("Tokyo");';
    say '# → これだけで済むようにしたい!';
}

まとめ

今回は、Adapterパターンだけでは解決できない課題を整理しました。

  • 呼び出し側にAdapter管理の責任が残っている
  • フォールバック処理を呼び出し側で実装する必要がある
  • 同じコードが複数箇所に散らばる(DRY原則違反)

これらの問題を解決するために、次回は「Facadeパターン」を導入します。

次回予告

次回は、WeatherFacadeクラスを実装します。複数のAdapterを内部で管理し、フォールバック機能を備えた統一インターフェースを提供します。呼び出し側のコードがどれだけシンプルになるか、お楽しみに!

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