こんにちは!FlutterやDartのコードを日々書いていく中で、「条件分岐やバリデーションのコードが、なんだか縦長に複雑になってしまうな……」と悩んだことはありませんか?
他の言語からDartに入ってきた方だと、どうしても従来の `if` 文や論理演算子 (`&&`, `||`) を並べた複雑なガード節を書きがちですよね。
でも、Dart 3で導入された「パターンマッチング(特に論理演算パターン)」を使いこなせるようになると、これまで何行も書いていた複雑なバリデーションロジックを、美しく直感的な1つの式にギュッと凝縮できるようになります。
ここをクリアすれば、あなたのDartコードは一段と洗練され、読み手の心を掴む美しいものになりますよ。一緒にマスターしていきましょう!
—
1. 従来のバリデーションが抱えていた「見通しの悪さ」
例えば、ユーザーからの入力データ(JSONやMapなど)を受け取り、その値が特定のビジネスルールを満たしているかチェックする場面を想像してください。
「年齢が18歳以上、かつ、ステータスが ‘active’ または ‘pending’ であること」という条件を、従来の書き方で実装するとこうなります。
bool validateUserLegacy(Map
final age = user[‘age’];
final status = user[‘status’];
// 従来のガード節や条件分岐
if (age is int && age >= 18) {
if (status == ‘active’ || status == ‘pending’) {
return true;
}
}
return false;
}
このコードでも動きますが、条件が複雑化するにつれて `if` のネストが深くなり、どこで何をチェックしているのか追いにくくなってしまいますよね。
—
2. Dart 3 論理演算パターンの基本
Dart 3では、値の構造や型をそのままパターンとして評価できる「パターンマッチング」が導入されました。その中でも、複数の条件を組み合わせるための強力な武器が 論理演算パターン です。
論理演算パターンには、以下の3つが用意されています。
1. ANDパターン (`p1 && p2`): 両方のパターンに一致する場合
2. ORパターン (`p1 || p2`): どちらか一方のパターンに一致する場合
3. NOTパターン (`!p`): 指定したパターンに一致しない場合
これらを `switch` 式や `if-case` 構文の中で組み合わせることで、複雑な条件分岐を驚くほどシンプルに表現できます。
—
3. 実践!複雑なバリデーションを1つの式に集約する
先ほどのユーザー検証ロジックを、Dart 3の `if-case` と論理演算パターンを使って書き直してみましょう。
bool validateUserModern(Map
// 論理演算パターン(ANDとOR)を1つの式に集約
if (user case {‘age’: int age && >= 18, ‘status’: ‘active’ || ‘pending’}) {
return true;
}
return false;
}
どうでしょう?コードの行数が減っただけでなく、「このデータがどういう構造と条件であれば許可されるのか」が一目でわかるようになっていますよね。
ここで使われているパターンの内訳を分解してみましょう。
- `{‘age’: int age && >= 18, …}`
- `age` というキーが存在し、その値が `int` 型であること (`int age`)。
- かつ、その値が `18` 以上であること (`>= 18`)。これが ANDパターン です。
- `…, ‘status’: ‘active’ || ‘pending’}`
- `status` の値が `’active’` または `’pending’` であること。これが ORパターン です。
このように、データの「型チェック」「値の抽出(バインディング)」「条件評価」を、パターンマッチングが同時に一撃でこなしてくれます。これがDartのコンパイラやVMにとっても非常に効率よく評価される洗練された仕組みなのです。
—
4. さらに複雑なケース:switch式との組み合わせ
実際の開発では、バリデーション結果に応じて異なるエラーメッセージや処理を返したいことが多いはずです。そんなときは `switch` 式と論理演算パターンを組み合わせると、さらに真価を発揮します。
String getValidationMessage(Map
return switch (user) {
// 正常系:年齢18歳以上かつ、activeかpending
{‘age’: int age && >= 18, ‘status’: ‘active’ || ‘pending’}
=> ‘検証成功:アクセスを許可します’,
// 年齢不足のケース
{‘age’: int age && < 18}
=> ‘エラー:18歳未満は利用できません’,
// ステータス異常のケース
{‘status’: var s} when s != ‘active’ && s != ‘pending’
=> ‘エラー:無効なステータスです’,
// 予期しないデータ構造や欠損がある場合
_ => ‘エラー:不正なデータフォーマットです’,
};
}
`switch` 式を使うことで、どの条件にヒットしたかが網羅的にわかり、かつ各ケースの意図が明確になります。
—
5. 陥りがちな文法エラーと注意点
ここで、初学者がハマりがちなポイントをいくつかシェアしておきますね。
① パターン内の変数宣言のスコープに注意する
ANDパターンやオブジェクトパターンの中で宣言した変数(例: `int age`)は、そのマッチが成功したスコープ内(右側の条件や実行ブロック)でのみ有効です。ORパターンの左右で異なる変数をバインドすることはできません(Dartの静的解析でエラーになります)。
② `||` (ORパターン)のルール
ORパターンの両側で宣言する変数は、両方の枝で同じ型・同じ名前である必要があります。片方だけに変数を作るとコンパイルエラーになりますので、「値の比較(リテラルや定数)」をベースに使うのが安全です。
// ❌ NGな例:片方だけに変数を作ろうとすると怒られます
// {‘status’: ‘active’ || String s}
// ⭕️ OKな例:特定の値との一致を比較する
{‘status’: ‘active’ || ‘pending’ || ‘banned’}
—
まとめ
今回は、Dart 3の論理演算パターンを活用して、複雑なバリデーションロジックを美しく簡略化するテクニックをご紹介しました。
- 従来の深い `if` ネストや煩雑な論理演算子の連鎖は、論理演算パターン (`&&`, `||`) でスマートに置き換えられる。
- `if-case` や `switch` 式と組み合わせることで、「型チェック」「値の抽出」「条件判定」をワンストップで行える。
- コードの可読性が劇的に上がり、バグの入り込む隙間を減らすことができる。
ここをクリアできれば、あなたのDartの引き出しは確実に一段深くなります。ぜひ今日のコードから、少しずつパターンマッチングを取り入れてみてくださいね。
次回の記事でも、現場で即座に使えるDartのディープな知見をお届けします。お楽しみに!