はじめに
前回は、シンプルな WeatherService クラスを作成しました。
今回は、別の天気情報サービスを追加してみます。しかし、この新しいサービスは WeatherService とは異なるメソッド名や戻り値の形式を持っています。2つのサービスを同時に使おうとすると、どのような問題が発生するのか体験していきましょう。
前回の振り返り
前回作成した WeatherService クラスは、以下の特徴を持っていました。
get_weather($city) メソッドで天気情報を取得- 戻り値は
{ condition => '晴れ', temperature => 25 } のようなハッシュリファレンス show_weather($city) メソッドで整形表示
今回の目標
第2回となる今回は、既存のレガシーな天気API OldWeatherAPI を追加します。このクラスは WeatherService とは異なるインターフェースを持っており、2つを同時に扱おうとすると問題が発生することを体験します。
新しい概念: 異なるインターフェース
今回学ぶ新しい概念は「異なるインターフェース」です。同じ目的(天気情報の取得)を持つクラスでも、メソッド名や戻り値の形式が異なる場合があります。
OldWeatherAPIクラスの登場
開発を進めていると、既存システムで使用されている古い天気APIクラスを発見しました。このクラスも天気情報を提供しますが、インターフェースが異なります。
OldWeatherAPIクラスの定義
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| package OldWeatherAPI {
use v5.36;
use Moo;
# メソッド名が異なる: get_weather ではなく fetch_weather_info
sub fetch_weather_info ($self, $location) {
my %data = (
'東京' => '晴れ/25度',
'大阪' => '曇り/23度',
'名古屋' => '晴れ/26度',
);
return $data{$location} // '情報なし';
}
}
|
このクラスの特徴を見ていきましょう。
- メソッド名が異なる:
get_weather ではなく fetch_weather_info - 引数名が異なる:
$city ではなく $location - 戻り値の形式が異なる: ハッシュリファレンスではなく、
'晴れ/25度' のような文字列
2つのサービスを使ってみる
では、WeatherService と OldWeatherAPI の両方を使って天気情報を表示しようとしてみましょう。
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
| #!/usr/bin/env perl
use v5.36;
# WeatherService クラス(前回と同じ)
package WeatherService {
use v5.36;
use Moo;
sub get_weather ($self, $city) {
my %weather_data = (
'東京' => { condition => '晴れ', temperature => 25 },
'大阪' => { condition => '曇り', temperature => 23 },
'札幌' => { condition => '雨', temperature => 18 },
);
return $weather_data{$city} // { condition => '不明', temperature => 0 };
}
sub show_weather ($self, $city) {
my $weather = $self->get_weather($city);
say "$city の天気: $weather->{condition}(気温: $weather->{temperature}℃)";
}
}
# OldWeatherAPI クラス(新しく追加)
package OldWeatherAPI {
use v5.36;
use Moo;
sub fetch_weather_info ($self, $location) {
my %data = (
'東京' => '晴れ/25度',
'大阪' => '曇り/23度',
'名古屋' => '晴れ/26度',
);
return $data{$location} // '情報なし';
}
}
# メイン処理
package main {
use v5.36;
my $new_service = WeatherService->new;
my $old_api = OldWeatherAPI->new;
say "=== 新しいサービス ===";
$new_service->show_weather('東京');
$new_service->show_weather('大阪');
say "";
say "=== 古いAPI ===";
# 問題1: メソッド名が違う
my $info = $old_api->fetch_weather_info('東京');
say "東京 の天気: $info";
$info = $old_api->fetch_weather_info('名古屋');
say "名古屋 の天気: $info";
}
|
実行結果:
1
2
3
4
5
6
7
| === 新しいサービス ===
東京 の天気: 晴れ(気温: 25℃)
大阪 の天気: 曇り(気温: 23℃)
=== 古いAPI ===
東京 の天気: 晴れ/25度
名古屋 の天気: 晴れ/26度
|
問題点の整理
上記のコードを見ると、2つのサービスを扱う際に以下の問題があることがわかります。
問題1: メソッド名が異なる
| サービス | メソッド名 |
|---|
| WeatherService | get_weather |
| OldWeatherAPI | fetch_weather_info |
同じ「天気情報を取得する」という目的なのに、呼び出し方が異なります。
問題2: 戻り値の形式が異なる
| サービス | 戻り値の形式 |
|---|
| WeatherService | ハッシュリファレンス { condition => '晴れ', temperature => 25 } |
| OldWeatherAPI | 文字列 '晴れ/25度' |
戻り値の扱い方も全く異なるため、呼び出し元のコードも変える必要があります。
問題3: 統一的な処理ができない
もし複数のサービスをループで処理したい場合、以下のような困ったコードになってしまいます。
1
2
3
4
5
6
7
8
| # これは動きません!
my @services = ($new_service, $old_api);
for my $service (@services) {
# どちらのメソッドを呼べばいい?
# 戻り値の形式をどう扱えばいい?
$service->show_weather('東京'); # OldWeatherAPI には show_weather がない!
}
|
なぜこの問題が起きるのか
この問題は、2つのクラスが異なるインターフェースを持っているために発生します。
- インターフェース = クラスが外部に公開するメソッドの名前、引数、戻り値の形式
同じ目的を持つクラスでも、開発時期や開発者が異なると、インターフェースが一致しないことはよくあります。特にレガシーシステムと新システムを統合する際に頻繁に遭遇する問題です。
今回の完成コード
今回作成した、2つのサービスを使おうとして困る完成コードを以下に示します。
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
| #!/usr/bin/env perl
use v5.36;
# WeatherService クラス
# 新しい天気情報サービス
package WeatherService {
use v5.36;
use Moo;
sub get_weather ($self, $city) {
my %weather_data = (
'東京' => { condition => '晴れ', temperature => 25 },
'大阪' => { condition => '曇り', temperature => 23 },
'札幌' => { condition => '雨', temperature => 18 },
);
return $weather_data{$city} // { condition => '不明', temperature => 0 };
}
sub show_weather ($self, $city) {
my $weather = $self->get_weather($city);
say "$city の天気: $weather->{condition}(気温: $weather->{temperature}℃)";
}
}
# OldWeatherAPI クラス
# レガシーな天気情報API(インターフェースが異なる)
package OldWeatherAPI {
use v5.36;
use Moo;
# メソッド名: fetch_weather_info(get_weatherではない)
# 引数名: $location($cityではない)
# 戻り値: 文字列(ハッシュリファレンスではない)
sub fetch_weather_info ($self, $location) {
my %data = (
'東京' => '晴れ/25度',
'大阪' => '曇り/23度',
'名古屋' => '晴れ/26度',
);
return $data{$location} // '情報なし';
}
}
# メイン処理
package main {
use v5.36;
my $new_service = WeatherService->new;
my $old_api = OldWeatherAPI->new;
say "=== 天気情報ツール ===";
say "";
say "【新サービス(WeatherService)】";
$new_service->show_weather('東京');
$new_service->show_weather('大阪');
$new_service->show_weather('札幌');
say "";
say "【旧API(OldWeatherAPI)】";
say "東京 の天気: " . $old_api->fetch_weather_info('東京');
say "大阪 の天気: " . $old_api->fetch_weather_info('大阪');
say "名古屋 の天気: " . $old_api->fetch_weather_info('名古屋');
say "";
say "--- 問題点 ---";
say "・メソッド名が異なる(get_weather vs fetch_weather_info)";
say "・戻り値の形式が異なる(ハッシュリファレンス vs 文字列)";
say "・統一的なループ処理ができない";
}
|
実行方法:
1
| perl weather_service_problem.pl
|
まとめ
今回は、異なるインターフェースを持つ2つの天気サービスを扱う際の問題を体験しました。
OldWeatherAPI は WeatherService とは異なるメソッド名・戻り値を持つ- 異なるインターフェースを持つクラスは、統一的に扱うことが難しい
- 呼び出し元のコードが複雑になり、保守性が低下する
次回予告
次回は、この問題を解決するための「橋渡しクラス」を作成します。OldWeatherAPI を WeatherService と同じインターフェースで使えるようにする方法を学びましょう!