こんにちは!FlutterやDartの開発現場で、日々コードの美しさとパフォーマンスを追求している先輩エンジニアです。
皆さんは、複数の条件が絡み合う複雑な「if文のネスト(山折り谷折りになったコード)」に直面して、「うわ、これ読むのしんどいな…」と頭を抱えた経験はありませんか?
他のプログラミング言語からDartに入ってきた方や、プログラミングを始めたばかりの頃は、どうしても条件分岐といえば `if-else` や `switch-case` を思い浮かべがちですよね。でも、Dart 3で導入されたパターンマッチングとガード句(`when`句)を使いこなせるようになると、あの読みにくかったコードが、まるで自然言語のように美しく、宣言的に生まれ変わるんです。
ここをクリアすれば、あなたのDartのコードは一段と洗練されますよ。さあ、一緒にその極意をマスターしていきましょう!
—
1. なぜ「ifのネスト」は悪者なのか?(Dart VMの視点から)
まず、なぜ条件分岐のネストを解消すべきなのか、少しだけ裏側の話をしますね。
人間がコードを読むとき、脳のワーキングメモリには限界があります。`if` が3回も4回もネストしていると、「今、自分はどの文脈のなかにいるんだっけ?」と迷子になってしまいますよね。
また、これはコードの可読性だけでなく、コンパイラやDart VMにとっても最適化のしやすさ(予測可能性)に影響します。複雑に絡み合った条件分岐は、CPUの分岐予測をミスさせやすく、パイプライン処理の効率を落とす原因にもなり得ます。
Dart 3のパターンマッチングは、単なる「見た目のシンタックスシュガー(お砂糖)」ではありません。データ構造と条件を同時にパッと検証し、コンパイル時に効率的なジャンプテーブルや分岐最適化を行える、非常に強力な武器なのです。
—
2. ガード句(when)の基本:パターンマッチングの「もう一歩先」へ
Dart 3の `switch` 式や `switch` 文では、値の形(パターン)を直接マッチさせることができます。しかし、「形は合っているけれど、中身の数値が〇〇以上の場合だけ処理したい」という、構造のパターンの先にある詳細な条件を絞り込みたいときがありますよね。
ここで登場するのが、ガード句(`when` 句)です。
イメージとしては、こんな感じです。
[データの形が一致するか?] ──YES──> [whenの条件式を満たすか?] ──YES──> [処理を実行!]
百聞は一見に如かず、具体的なコードを見てみましょう。オンラインショップの割引システムを例にしますね。
従来の `if-else` による泥臭い実装(ビフォー)
double calculateDiscount(String userType, int purchaseAmount, bool isFirstTime) {
if (userType == ‘VIP’) {
if (purchaseAmount >= 10000) {
return 0.3; // VIPで1万円以上なら30%オフ
} else {
return 0.2; // VIPで1万円未満なら20%オフ
}
} else if (userType == ‘Regular’) {
if (isFirstTime) {
return 0.15; // 通常会員で初回なら15%オフ
} else {
return 0.05; // 通常会員の通常時は5%オフ
}
} else {
return 0.0; // ゲストは割引なし
}
}
うーん、`if` が重なっていて、条件の全体像を把握するのに頭を使いますよね。
ガード句 (`when`) を使った洗練された実装(アフター)
これを Dart 3 の `switch` 式とガード句を使って書き換えてみましょう。
double calculateDiscountOptimized(String userType, int purchaseAmount, bool isFirstTime) {
return switch ((userType, purchaseAmount, isFirstTime)) {
// VIP会員の条件分岐
(‘VIP’, >= 10000, _) => 0.3,
(‘VIP’, _, _) => 0.2,
// 通常会員の条件分岐(ガード句の登場!)
(‘Regular’, _, true) => 0.15,
(‘Regular’, _, false) => 0.05,
// それ以外(ゲストなど)
_ => 0.0,
};
}
おっと、今回はレコード(Record)を使ったパターンですが、より「ガード句(`when`)」の真価を発揮する例を見てみましょう。複数の変数や複雑な論理演算を伴う場合です。
—
3. 実践!複雑なビジネスロジックをガード句で美しく解きほぐす
例えば、配送システムの送料を計算するロジックを考えてみます。
条件は以下の通りです。
1. 重量(kg)と地域(zone)で基本料金が決まる。
2. ただし、「合計金額が5000円以上、かつ重量が2kg未満」の場合は、特別に送料が無料になる。
3. 「プレミアム会員」であれば、地域に関わらず一律で送料が割引になる。
これをガード句(`when`)を使って書いてみます。
class ShippingContext {
final String zone;
final double weightKg;
final int orderAmount;
final bool isPremiumUser;
ShippingContext({
required this.zone,
required this.weightKg,
required this.orderAmount,
required this.isPremiumUser,
});
}
double calculateShippingFee(ShippingContext ctx) {
return switch (ctx) {
// ガード句を活用した「5000円以上かつ2kg未満」の特例ルール
// どんなオブジェクト構造であっても、whenで詳細な条件を追加できます
_ when ctx.orderAmount >= 5000 && ctx.weightKg < 2.0 => 0.0,
// プレミアム会員のルール
_ when ctx.isPremiumUser => 300.0,
// 通常エリアごとのルール
(ShippingContext ctx) when ctx.zone == ‘A’ => 600.0,
(ShippingContext ctx) when ctx.zone == ‘B’ => 900.0,
// フォールバック(どこにも当てはまらない場合)
_ => 1200.0,
};
}
どうですか? 条件の優先順位が上から下へ上手に整理されていて、非常に読みやすいですよね。
`when` 句を使うことで、「このパターンにマッチした上で、さらにこのbooleanの条件が真(true)のときだけ採用する」という細かいコントロールが可能になるのです。
—
4. 陥りがちな文法エラーと注意すべきポイント
ここで、初学者の皆さんがハマりがちなポイントをいくつかシェアしておきますね。これを覚えておけば安心です。
注意点1: `switch` 文と `switch` 式(Expression)の書き方の違いに注意!
Dartでは、`switch (x) { … }` を変数に代入したり `return` の右側に書く「switch式」と、文として実行する「switch文」があります。
上の例で使っているのは `switch式` です。式の場合は、各ケースの終わりにカンマ `,` が必要になり、アロー構文 `=>` を使って値を直接返します。
注意点2: ガード句の評価順序は「上から下」
Dart VMは、パターンマッチングとガード句を上から順番に評価します。
もし、より具体的な条件(例: `when ctx.orderAmount >= 5000`)よりも、大雑把な条件(例: `_ => 1200.0`)を上に書いてしまうと、望まない結果になってしまいます。
「特殊な条件(詳細な条件)を上に、一般的な条件を下に書く」 これが鉄則です。
注意点3:網羅性チェック(Exhaustiveness checking)の罠
Dartの強力な機能に「型の網羅性チェック」があります。すべてのケースがカバーされていないと、コンパイルエラー(あるいは警告)になります。
しかし、`when` 句を用いた条件分岐は、Dartのコンパイラから見ると「実行時にならないと真偽がわからない条件」が含まれるため、網羅性の判定が複雑になります。そのため、最後に必ずデフォルトケース `_ => …` を用意しておくのが安全かつ確実な設計のコツです。
—
まとめ:ガード句でコードを「宣言的」に書こう
今回は、Dart 3のパターンマッチングとガード句(`when`)を活用した、複雑な条件分岐の最適化について解説しました。
- if文のネストの地獄から抜け出し、コードの可読性を劇的に高められる。
- パターンの形だけでなく、`when` 句を使うことで動的な条件をエレガントに絞り込める。
- 上から下へ評価される特性を活かし、特殊な条件から順に記述する。
この書き方に慣れてくると、従来の `if-else` だらけのコードに戻れなくなるくらい、心地よさを感じるはずです。
ここをクリアできれば、あなたのDartの基本はもうバッチリマスターできていますよ!自信を持って、日々の開発やFlutterアプリのロジック実装に活かしていってくださいね。
それでは、また次回の深掘り記事でお会いしましょう!Happy coding!