【実務・中級編】Dart 3における「論理演算パターン」の組み合わせ:複雑なバリデーションロジックの簡略化 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

コードレビューの時間だ。その「ifの巣窟」、まだ放置するつもりか?

プロダクションコードのレビューをしていて、最もエンジニアの技量が露呈するのはどこか知っているか?
それは、複雑な業務要件をさばく「バリデーションロジック」の記述だ。

例えば、フロントエンドから受け取ったフォーム入力値や、API連携で飛んできた非構造化データ(JSON)を検証する際、以下のようなコードを書いていないだろうか。

// 【アンチパターン】ネストが深く、認知負荷が極限まで高いコード
bool validateUser(Object? data) {
if (data is Map) {
if (data.containsKey(‘role’) && data.containsKey(‘permissions’)) {
var role = data[‘role’];
var perms = data[‘permissions’];
if (role == ‘admin’ || role == ‘super_user’) {
if (perms is List && perms.contains(‘write’)) {
return true;
}
} else if (role == ‘editor’) {
if (perms is List && (perms.contains(‘write’) || perms.contains(‘publish’))) {
return true;
}
}
}
}
return false;
}

おいおい、正気か? このコードには、可読性の欠如、保守性の低さ、そして何よりDart 3がもたらした革命的な言語機能を完全に無視しているという致命的な罪がある。

ネットの適当な入門記事をなぞっただけのコードは、今日で終わりにしよう。
今回は、Dart 3の「論理演算パターン(Logical Patterns)」を駆使し、上記のような複雑怪奇なバリデーションを、美しく、型安全で、コンパイラ最適化の恩恵を受けられる単一の式へと昇華させる極意を伝授する。

—

Dart 3 パターンマッチングの核心:論理演算パターンとは何か

Dart 3以降、パターンマッチングは単なる「データ構造の分解(Destructuring)」の道具にとどまらない。`switch` 式や `if-case` 文の中で、`and` や `or` といった論理演算子を組み合わせることで、「値の形状(Shape)と条件(Condition)を同時に検証する強力な述語(Predicate)」として機能する。

論理演算パターンには、以下の3つが存在する。

1. AND パターン (`sub1 && sub2`): 両方のパターンに一致する場合に成功する。
2. OR パターン (`sub1 || sub2`): いずれかのパターンに一致する場合に成功する。
3. NOT パターン (`!sub`): 指定したパターンに一致しない場合に成功する。

これらを組み合わせることで、従来の命令型プログラミング(`if` のネストやフラグ変数の多用)を駆逐し、宣言型で数学的に厳密なバリデーションパイプラインを構築できる。

—

実践:複雑な権限バリデーションを「1つの式」に畳み込む

先ほどのアンチパターンを、Dart 3の論理演算パターンを用いてプロダクション品質にリファクタリングしよう。

フロントエンドのコンポーネント設計や、APIクライアント層でそのまま使える堅牢なコードを見てほしい。

// 【プロダクション品質】Dart 3 論理演算パターンを駆使した堅牢なバリデーション
sealed class AuthState {}
class Unauthenticated extends AuthState {}
class Authenticated extends AuthState {
final String role;
final List permissions;
Authenticated(this.role, this.permissions);
}

bool authorizeRequest(Object? rawData) {
// if-case文とパターンマッチングによる宣言的バリデーション
if (rawData case
// 1. まずMap型であり、必要なキーが存在することを担保(AND)
Map json &&
{‘role’: var role, ‘permissions’: var perms} &&

// 2. role と perms の組み合わせに対するビジネスロジック(OR / AND)
(
// パターンA: admin または super_user で、かつ ‘write’ 権限を持つ
( (String r && (r == ‘admin’ || r == ‘super_user’)) &&
(List p && p.contains(‘write’)) )
||
// パターンB: editor で、かつ ‘write’ または ‘publish’ 権限を持つ
( (String r && r == ‘editor’) &&
(List p && (p.contains(‘write’) || p.contains(‘publish’))) )
)
) {
// コンパイラはこのブロック内において、role が String、perms が List であることを完全に把握している(Type Promotion)
return true;
}

return false;
}

このコードが「圧倒的に優れている」理由

1. 認知的負荷の劇的な削減:
条件分岐のネストが完全に消え、`if (rawData case …)` という単一の「検証パイプライン」に集約されている。コードを読む人間は、上から下へ視線を移動させるだけで、どのようなデータ構造と権限の組み合わせが許可されるのかが一目で理解できる。
2. 網羅性と型の昇格(Type Promotion):
Dart VMおよびAOTコンパイラは、パターンマッチングが成功した瞬間に厳密な型推論を行う。ブロック内であらためて `as List` や `is Map` といった冗長なキャストを書く必要は一切ない。
3. バグの温床の排除:
従来の `if` 文では、nullチェックの漏れや `KeyError` が実行時例外を引き起こすリスクがあった。パターンマッチングでは、キーの存在証明と変数のバインドがアトミック(不可分)に行われるため、安全性が極めて高い。

—

パフォーマンスとコンパイル時の挙動:Dart VMはどう評価するか?

「こんな複雑なパターンを書くと、実行時コスト(オーバーヘッド)が高くなるのではないか?」
そう懸念したエンジニアは、Dartの挙動をよく理解している優秀な読者だ。

結論から言えば、心配無用だ。

DartのAOT(Ahead-Of-Time)コンパイラおよびJIT(Just-In-Time)のCFA(Control Flow Analysis)は、パターンマッチングの構文木(AST)を解析し、極めて効率的なジャンプ命令と型チェックのシーケンスにコンパイルする。
手動で記述したネストした `if` 文と比較して、実行時パフォーマンスのペナルティは実質的にゼロ、あるいは最適化の恩恵によりむしろ向上することすらある。

ただし、1点だけ注意すべき設計上のアンチパターンがある。

⚠️ アンチパターン:複雑すぎるパターンによる可読性の逆転

論理演算パターンは強力だが、1つの `case` 文の中に業務ロジックを詰め込みすぎると、今度は「数学の数式のように難解すぎるコード」になり、保守性が落ちる。

もし条件がさらに肥大化する場合は、以下のようにプライベートなヘルパーパターンやガード節(`when` 句)に切り出すのが、シニアアーキテクトとしての正しいアプローチだ。

// 許容されるロールの定義を定数またはゲッターに切り出す
bool _isAdminRole(Object? role) => role == ‘admin’ || role == ‘super_user’;
bool _isEditorRole(Object? role) => role == ‘editor’;

bool authorizeRequestClean(Object? rawData) {
if (rawData case Map json &&
{‘role’: var role, ‘permissions’: var perms}) {

// ガード節や明確な関数呼び出しと組み合わせることで、複雑性をカプセル化する
if (_isAdminRole(role) && perms case List p when p.contains(‘write’)) {
return true;
}
if (_isEditorRole(role) && perms case List p when p.contains(‘write’) || p.contains(‘publish’)) {
return true;
}
}
return false;
}

※ `when` 句(ガード)を併用することで、パターンマッチングの「形状検証」と、通常のboolean条件を美しく分離できる。

—

まとめ:明日からのコードレビューで使うべき視点

Dart 3のパターンマッチングは、単なる「モダンアピール機能」ではない。それは、アプリケーションの信頼性を型システムとコンパイラの強制力によって担保するための最強の武器だ。

もし君が所属するチームのコードベースに、まだ深いネストを持つ `if` や、冗長な型キャストの山があれば、それはリファクタリングの絶好の機会だ。

1. 「データ構造の検証」と「ビジネスロジックの判定」を分離せず、AND/ORパターンで1つの式に統合できないか思考する。
2. コンパイラの型推論(Type Promotion)を信じ、無駄なキャストを排除する。
3. 複雑になりすぎた場合は、条件を小さく分解し、`when` 句や適切な関数分割でカプセル化する。

この知見を実装に落とし込み、チームのコードベースを次のステージへと引き上げてくれ。
健闘を祈る。

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