🐮

[🔰初心者向け🔰]Flutter/Dartで学ぶプログラミング ~エンジニアの夫が帰ってきません! ~

に公開

こんにちは、今回はすでにタイトルから察している人も多いかと思いますがエンジニア界隈で語り継がれる「スーパーから帰ってこない夫」のジョーク を元にFlutter(Dart)で「仕様」「エラー処理」「UX」についてゆるっと考察していくネタ記事です!

気軽に読んでください😉

夫がスーパーから帰ってこない!

妻:「ねぇ、帰りにスーパー寄れる?」
夫(エンジニア):「うん、いいよ~」
妻∶「じゃあ卵買ってきて。牛乳があったら2本ね」
夫(エンジニア): 「了解!😎」
このあと夫はスーパー内をぐるぐる回るだけで家に帰ることはなかった...

これはよくあるエンジニアジョークなのですがこれは妻の指示(仕様)が曖昧であったことに原因があります。
現状の妻の指示(仕様)をdartに落とし込むと...

void main() {
  bool milkIsAvailable = false; // 牛乳が売ってない
  bool milkRequired = true;     // 牛乳があれば2本買って

  buy('卵');

  // 牛乳があれば買う(でも曖昧な仕様)
  if (milkRequired) {
    while (!milkIsAvailable) {
      print('牛乳を探しています...');
      // 見つからないから無限ループ
    }

    buy('牛乳を2本');
  }

  print('帰宅します');
}

void buy(String item) {
  print('$item を買いました');
}

これを実行すると牛乳が売っていない=falseなので無限ループしてしまいます。バグの発生です!
つまり夫はスーパーをぐるぐる回るだけでスーパーから帰ってくることはありません。
ではどうすれば良いか。
まず妻の要件を変更することにしましょう。
「卵を買ってきて。牛乳があったら2本買ってきて。なかったら牛乳はいらないよ。」
これだとない場合の動きが定義されたので

  if (milkIsAvailable) {
    buy('牛乳を2本');
  } else {
    print('牛乳はなかったのでスキップします');
  }
  print('帰宅しました');

やった!夫は帰宅することができました!

もっと要件を複雑化してみよう!

妻:「じゃあ、卵買ってきて。あと牛乳、あったら2本だけど、なかったら豆乳でいいよ」
さぁどんどんネストが深くなってきてしまいました。
こうなると今度は保守性や可読性が下がってしまいます...
しかしDartではenumで状態を定義し、switch文で分岐をスマートに書けます。

enum MilkStatus {
  available,
  unavailable,
}

void main() {
  final milkStatus = MilkStatus.unavailable;

  buy('卵');

  switch (milkStatus) {
    case MilkStatus.available:
      buy('牛乳を2本');
      break;
    case MilkStatus.unavailable:
      buy('豆乳を2本');
      break;
  }

  print('帰宅しました');
}
///省略

こうするとネストが深くならずにどこでなにが行われているのかがわかりやすくなりエラーやバグが発生しにくくなります。

実際にバグを起こさないために

今回は分かりやすさを重視して、あえて「永遠に牛乳を探し続ける」ループ処理にしましたが、実際の開発でこれと似たような構造を無意識に組み込んでしまうケースは意外と多いです。
特に、FirebaseやREST APIを使った通信処理では、エラーハンドリングが不十分だと、次のようなリスクが生まれます:

  • 成功するまで何度も同じリクエストを繰り返す
  • サーバーからの404や500を無視して無限リトライ
  • 最悪の場合、ユーザーに「何が起こったのか」を一切伝えられない

RiverpodやDioといったライブラリの力を借りて、ちゃんとエラーを分岐・通知する仕組みをつくるのが理想です。

try {
  final response = await dio.get('/milk');
  if (response.statusCode == 200) {
    // 成功:牛乳買えた
  } else if (response.statusCode == 404) {
    // 牛乳が見つからなかった → ユーザーに伝える
  } else {
    // その他のサーバーエラーなど
  }
} catch (e) {
  // ネットワークが切れたなどの例外処理
}

Flutterなどのアプリ開発の場合なにがおこったのかユーザー(妻)に伝えなければなりません。

  • 牛乳がなかった(404)
  • ネットワークに繋がっていない(例外)
  • 処理中である(ローディング)

こういったフィードバックを加えて情報を伝えることでUI/UX向上につながるのではないかと考えます。
またenumとswitch文を使用してstatusコードに対してユーザーにどう伝えるかやアイテムの種類によっての動作を切り替えやすくなり、保守性が向上します。

さいごに

「こんな人現実にいたらヤバすぎる!」って思うかもしれませんがそこはエンジニアジョークということで...
でもこの「牛乳がなかったらどうする問題」実は意外とバグの本質を突いた話でもあります。実装の際に条件分岐やエラーハンドリングが曖昧だと思わぬ挙動を引き起こすことになります。
最近ではAIの支援でこうした問題は多少減ってきたとはいえ「想定外」は常に起こるものとして実装をしていけたら良いですね。

今回は夫がスーパーから帰ってこないをdartに落とし込んで考えてみました!

ぽちぽちのつどい

Discussion