【入門編】Dartの「パターンマッチング」と「従来のif-else」:分岐の深さと可読性のトレードオフ – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!FlutterやDartのコードを日々書き進める中で、「なんだか最近、if文のネストが深くなってきて見通しが悪いな……」と感じたことはありませんか?

条件分岐が複雑になると、コードの「認知負荷(脳みそが理解するために使うエネルギー)」が一気に跳ね上がり、バグの温床になりますよね。

今回は、Dart 3で導入された「パターンマッチング」を武器に、従来の深すぎる`if-else`地獄をどのように美しく、そして堅牢にリファクタリングできるのか、その極意を一緒に紐解いていきましょう!ここをクリアすれば、あなたのDartコードの表現力は一段とプロフェッショナルになりますよ。

—

1. 従来の `if-else` が抱える「認知の限界」

まずは、よくある実務のシーンを想像してみてください。
例えば、APIから受け取ったユーザーのステータスや権限に応じて、画面に表示するメッセージを切り替える処理です。

従来の`if-else`や`switch-case`文でこれを書くと、どうしてもこうなってしまいますよね。

String getAccessMessage(Map user) {
if (user.containsKey(‘role’)) {
if (user[‘role’] == ‘admin’) {
if (user[‘isActive’] == true) {
return ‘管理者としてアクセス中’;
} else {
return ‘アカウントが凍結された管理者です’;
}
} else if (user[‘role’] == ‘guest’) {
return ‘ゲストユーザーです’;
} else {
return ‘一般ユーザーです’;
}
} else {
return ‘不正なユーザーデータです’;
}
}

何が問題なのか?

  • 右へ右へと流れる「矢印型」のコード: ネストが深くなるにつれ、画面の右側にコードが押しやられ、閉じ括弧 `}` の対応関係を追うだけで目が回ってしまいます。
  • ガード条件の分散: 「本当にそのキーが存在するか?」というチェックと、「値が何であるか」の条件がバラバラに散らばり、全体像を把握するのに脳内メモリを大量消費します。

コンパイラやDart VMの視点から言えば、これらは単純なジャンプ命令にコンパイルされますが、「人間が読むときのコスト」が非常に高いのが最大の欠点です。

—

2. Dart 3 パターンマッチングによる構造的解放

Dart 3で導入されたパターンマッチング(特に `switch` 式や `if-case`)は、「データの形(構造)をそのままパターンとして記述し、一撃で値を取り出して分岐する」ための強力な仕組みです。

先ほどの複雑なロジックを、Dart 3のパターンマッチングを使って書き換えてみましょう。

String getAccessMessageModern(Object? data) {
// パターンマッチングを使った switch 式
return switch (data) {
// 1. 管理者かつアクティブな場合
{‘role’: ‘admin’, ‘isActive’: true} => ‘管理者としてアクセス中’,

// 2. 管理者だが非アクティブな場合
{‘role’: ‘admin’, ‘isActive’: false} => ‘アカウントが凍結された管理者です’,

// 3. ゲストの場合
{‘role’: ‘guest’} => ‘ゲストユーザーです’,

// 4. その他のMapデータ
{‘role’: String _} => ‘一般ユーザーです’,

// 5. データ構造が想定と違う場合(デフォルト)
_ => ‘不正なユーザーデータです’,
};
}

どうでしょう?この圧倒的なスッキリ感!
コードが右に流れることなく、まるで「条件の表」を見ているかのように上から下へと直感的に読めるようになりますよね。これがパターンマッチングの真骨頂です。

—

3. 実務で即効性バツグン!「構造化データ」の分解と束縛

パターンマッチングの素晴らしいところは、条件分岐と同時に「データのな中身を取り出して変数にバインド(束縛)できる」点にあります。

例えば、独自のクラス(レコードやオブジェクト)を扱うときの例を見てみましょう。

陥りがちな文法エラーと注意点

初学者のうちによくあるのが、パターンマッチングの構文と従来の`switch`文の混同です。Dart 3の `switch` は「式(Expression)」として使えるため、直接値を返すことができます。

sealed class Result {}
class Success extends Result {
final String data;
Success(this.data);
}
class Error extends Result {
final int errorCode;
Error(this.errorCode);
}

// 綺麗にハンドリングする例
String handleResult(Result result) {
return switch (result) {
// Successクラスのインスタンスであれば、中身の ‘data’ を直接変数 msg に取り出す
Success(data: var msg) => ‘成功: $msg’,

// Errorクラスのインスタンスであれば、errorCodeを取り出す
Error(errorCode: 404) => ‘エラー: ページが見つかりません’,
Error(errorCode: var code) => ‘エラーが発生しました (コード: $code)’,
};
}

ここで、もし `sealed` クラス(網羅性が保証されるクラス)を使っている場合、すべてのパターンを網羅していないと、Dartのコンパイラがビルド時にエラーを教えてくれます。
「あ、新しいステータスを追加したのに、分岐を書き忘れた!」というヒューマンエラーをコンパイル段階で完全に防げるのは、チーム開発において非常に心強いポイントですね。

—

4. まとめ:深さから解放され、本質的なロジックに集中しよう

今回は、Dartの「パターンマッチング」と従来の「`if-else`」を比較し、コードの認知負荷をいかに下げるかについて解説しました。

  • 従来の `if-else`: ネストが深くなりやすく、データの構造と条件が分散して読みにくい。
  • Dart 3 パターンマッチング: データの形をそのまま記述でき、フラットで美しい分岐と安全な変数抽出を同時に実現できる。

「条件分岐が3段階以上深くなってきたな」と感じたら、それはパターンマッチング(`switch` 式や `if-case`)へのリファクタリングのサインです。

ここをクリアすれば、あなたの書くDartコードは一気に洗練され、メンテナンス性の高い素晴らしいものになりますよ。ぜひ今日のコードから取り入れてみてくださいね!

タイトルとURLをコピーしました