【入門編】Dartの「パターンマッチング」で実装するコマンドパターンのクリーンな実装 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは。Dartの世界へようこそ。
Dartのアーキテクトとして、日々コンパイラやランタイムと対話していると、「いかにコードを堅牢にし、かつメンテナンスしやすくするか」という問いに帰結します。

今回は、多くの開発者がオブジェクト指向の設計で「サブクラス地獄」に陥りがちなコマンドパターンを、Dart 3の強力な武器である「レコード」と「パターンマッチング」を使って、驚くほどクリーンに書き換える手法を伝授します。

—

1. コマンドパターンの「伝統的」な悩み

これまでのDart(あるいはJavaやC#)では、コマンドパターンを実現するために、親クラスを作り、そこから派生するクラスを大量に定義していました。

// 昔ながらのコマンドパターン
abstract class Command { void execute(); }
class AddCommand extends Command { … }
class DeleteCommand extends Command { … }

これだと、コマンドの種類が増えるたびにファイルが分かれ、管理コストが爆発しますよね。実は、Dart 3以降、この設計は「古い」のです。

2. Dart 3でコマンドを「データ」として捉える

Dart 3では、レコード型 `(type, payload)` を使うことで、コマンドを「クラス」ではなく「データそのもの」として扱えるようになりました。これにより、コマンドの定義はたった1行の型定義で済みます。

実装例:シンプルなコマンド処理

// コマンドをレコード型で定義する
// (String, {int value}) のように名前付き引数も使えます
typedef Command = (String action, Map params);

void handleCommand(Command command) {
// パターンマッチングで分岐!
switch (command) {
case (‘add’, {‘value’: int v}):
print(‘追加: $v’);
case (‘remove’, {‘id’: int id}):
print(‘削除: $id’);
case (var action, _):
print(‘未知のコマンド: $action’);
}
}

void main() {
handleCommand((‘add’, {‘value’: 10})); // 出力: 追加: 10
handleCommand((‘remove’, {‘id’: 1})); // 出力: 削除: 1
}

このコードの何が凄いのか?

  • メモリ効率: クラスのインスタンス化に伴うオーバーヘッドを削減できます。
  • 可読性: `switch` 文がそのまま「設計図」になります。
  • 網羅性チェック: もし `switch` で漏れがあると、Dartコンパイラが警告を出してくれます。「安全」であることは、プログラミングにおいて最大の機能です。

3. なぜ「パターンマッチング」が強力なのか

初心者がよく陥る罠に、`if-else` の入れ子があります。パターンマッチングの最大の強みは、「値の構造を分解(デストラクト)しながら条件判定ができる」点にあります。

注意すべき「文法エラー」のポイント

パターンマッチングを使う際、以下の点に注意してください。

1. 型の厳密性: `case (‘add’, {‘value’: int v})` と書いたとき、`value` キーが存在しないレコードが渡されるとマッチしません。
2. ワイルドカード `_`: `switch` 文の最後には必ず `_`(デフォルトケース)を置いてください。これを怠ると、コンパイラは「網羅性が不完全だ」と判断してエラーにします。
3. 定数判定: パターン内では変数を宣言できますが、比較対象は定数である必要があります。

4. アーキテクトからのアドバイス

サブクラスを作るべき時と、レコード+パターンマッチングを使うべき時の境界線を知っておきましょう。

  • クラスを使うべき時: そのコマンドが「複雑なロジック」や「状態」を保持し、単体でテストしたい場合。
  • レコードを使うべき時: 今回のように「コマンドを投げ、結果を受け取る」といった、データの受け渡しが主目的の場合。

モダンなDart開発では、「ロジックは純粋関数に、データはレコードに」寄せるのが、最もバグを生みにくく、かつIsolate間でのデータ転送(シリアライズ)もしやすい設計になります。

—

最後に:ここをクリアすれば、Dartマスターです

パターンマッチングを使いこなすと、コードから「ボイラープレート(定型文)」が消え去り、「何が行われているか」という本質だけが浮かび上がります。

最初はレコードの型定義に戸惑うかもしれませんが、一度慣れてしまえば、もう昔のような冗長なクラス定義には戻れなくなるはずです。

「Dartの言語仕様は、あなたの書くコードをより安全に、より速くするために進化している」。この感覚を忘れずに、ぜひ現場のコードで試してみてください。

次は、「密封クラス(Sealed Class)」と組み合わせた、より高度なステート管理についてお話ししましょうか。それでは、素晴らしいコーディングライフを!

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