@nqounetです。
前回は、if/elseを使ってCSVとJSONの2つの形式でアドレス帳データを出力できるようにしました。
今回は、新しい形式を追加するとコードがどうなるか見ていきましょう。
YAMLとXML形式も追加したい
お客様から「YAML形式とXML形式でも出力したい」というリクエストが来ました。
さっそく対応してみましょう。前回のif/else文に新しい条件を追加します。
flowchart TD
A[開始] --> B{format は csv?}
B -->|Yes| C[CSV出力]
B -->|No| D{format は json?}
D -->|Yes| E[JSON出力]
D -->|No| F{format は yaml?}
F -->|Yes| G[YAML出力]
F -->|No| H{format は xml?}
H -->|Yes| I[XML出力]
H -->|No| J[エラー]
C --> K[終了]
E --> K
G --> K
I --> K
J --> K
style B fill:#fdd
style D fill:#fdd
style F fill:#fdd
style H fill:#fdd
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
| #!/usr/bin/env perl
use v5.36;
use JSON::PP;
use YAML::Tiny;
# アドレス帳データ
my @contacts = (
{ name => '田中太郎', email => 'tanaka@example.com', phone => '090-1234-5678' },
{ name => '鈴木花子', email => 'suzuki@example.com', phone => '080-2345-6789' },
{ name => '佐藤次郎', email => 'sato@example.com', phone => '070-3456-7890' },
);
# コマンドライン引数から形式を取得(デフォルトはcsv)
my $format = $ARGV[0] // 'csv';
# 形式に応じて出力を切り替え
if ($format eq 'csv') {
# CSV形式で出力
say "name,email,phone";
for my $contact (@contacts) {
say "$contact->{name},$contact->{email},$contact->{phone}";
}
}
elsif ($format eq 'json') {
# JSON形式で出力
my $json = JSON::PP->new->pretty->encode(\@contacts);
print $json;
}
elsif ($format eq 'yaml') {
# YAML形式で出力
my $yaml = YAML::Tiny->new(\@contacts);
print $yaml->write_string;
}
elsif ($format eq 'xml') {
# XML形式で出力
say '<?xml version="1.0" encoding="UTF-8"?>';
say '<contacts>';
for my $contact (@contacts) {
say ' <contact>';
say " <name>$contact->{name}</name>";
say " <email>$contact->{email}</email>";
say " <phone>$contact->{phone}</phone>";
say ' </contact>';
}
say '</contacts>';
}
else {
die "未対応の形式です: $format\n";
}
|
動作確認
YAML形式で出力:
1
2
| cpanm YAML::Tiny # 初回のみ
perl exporter.pl yaml
|
出力結果:
1
2
3
4
5
6
7
8
9
10
| ---
- email: tanaka@example.com
name: 田中太郎
phone: 090-1234-5678
- email: suzuki@example.com
name: 鈴木花子
phone: 080-2345-6789
- email: sato@example.com
name: 佐藤次郎
phone: 070-3456-7890
|
XML形式で出力:
出力結果:
1
2
3
4
5
6
7
8
9
| <?xml version="1.0" encoding="UTF-8"?>
<contacts>
<contact>
<name>田中太郎</name>
<email>tanaka@example.com</email>
<phone>090-1234-5678</phone>
</contact>
...
</contacts>
|
動きましたね!でも、コードを見てください…
if/elseが肥大化している!
コードが長くなってきました。問題点を整理してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
| ┌──────────────────────────────────────────────────────────────┐
│ exporter.pl │
├──────────────────────────────────────────────────────────────┤
│ if (csv) { CSV出力処理... } │
│ elsif (json) { JSON出力処理... } │
│ elsif (yaml) { YAML出力処理... } ← どんどん増える! │
│ elsif (xml) { XML出力処理... } │
│ elsif (tsv) { ... } │
│ elsif (markdown) { ... } │
│ elsif (html) { ... } │
│ ... │
└──────────────────────────────────────────────────────────────┘
|
問題1: コードが長くなる
形式が増えるたびにelsifブロックが増えていきます。今は4形式ですが、TSV、Markdown、HTML…と増えていくと、このファイルは何百行にもなるでしょう。
問題2: 変更時の影響範囲が大きい
CSV出力の処理を修正したいだけなのに、JSON、YAML、XMLの処理も同じファイル内にあります。うっかり別の形式のコードを壊してしまうかもしれません。
問題3: テストが難しい
CSV出力だけをテストしたくても、このファイル全体を読み込む必要があります。
問題4: 新しい形式の追加が怖い
既存のコードに手を入れるので、動いている部分を壊してしまう可能性があります。
これが「コードの臭い」です
このような問題をコードの臭い(Code Smell)と呼びます。
今回のケースは「長いメソッド(Long Method)」や「分岐の多さ(Switch Statements)」という典型的なコードの臭いです。
コードの臭いは、今すぐバグになるわけではありませんが、将来的に問題を引き起こす兆候です。
どうすれば良いのか?
次回から、このコードを改善していきます。
まずは「出力処理を別のクラスに分ける」ことから始めましょう。そうすることで、各形式の処理が独立し、変更や追加がしやすくなります。
今回の完成コード
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
| #!/usr/bin/env perl
use v5.36;
use JSON::PP;
use YAML::Tiny;
# アドレス帳データ
my @contacts = (
{ name => '田中太郎', email => 'tanaka@example.com', phone => '090-1234-5678' },
{ name => '鈴木花子', email => 'suzuki@example.com', phone => '080-2345-6789' },
{ name => '佐藤次郎', email => 'sato@example.com', phone => '070-3456-7890' },
);
# コマンドライン引数から形式を取得(デフォルトはcsv)
my $format = $ARGV[0] // 'csv';
# 形式に応じて出力を切り替え
if ($format eq 'csv') {
# CSV形式で出力
say "name,email,phone";
for my $contact (@contacts) {
say "$contact->{name},$contact->{email},$contact->{phone}";
}
}
elsif ($format eq 'json') {
# JSON形式で出力
my $json = JSON::PP->new->pretty->encode(\@contacts);
print $json;
}
elsif ($format eq 'yaml') {
# YAML形式で出力
my $yaml = YAML::Tiny->new(\@contacts);
print $yaml->write_string;
}
elsif ($format eq 'xml') {
# XML形式で出力
say '<?xml version="1.0" encoding="UTF-8"?>';
say '<contacts>';
for my $contact (@contacts) {
say ' <contact>';
say " <name>$contact->{name}</name>";
say " <email>$contact->{email}</email>";
say " <phone>$contact->{phone}</phone>";
say ' </contact>';
}
say '</contacts>';
}
else {
die "未対応の形式です: $format\n";
}
|
まとめ
- 新しい形式を追加するとif/elseが肥大化することを体験しました
- コードが長くなると、保守性やテスト容易性が低下します
- このような問題を「コードの臭い」と呼びます
- 次回から、この問題を解決するためのリファクタリングを始めます
次回「第3回-出力処理を専用クラスに分けよう」では、出力処理を専用クラスに分けていきます。お楽しみに!