Flutterでif文を減らすためのデータ駆動型アプローチ

ENSPORTS
  • 開発
Yuto Mori Yuto Mori

アプリ開発をしている中、とくに機能がリッチになるにつれて、UIの表示を切り替えるための条件分岐が増えていきます。

if-else ifが延々と連なり、コードがどんどん縦に長くなるにつれ、可読性や保守性も下がっていきます。

そこで今回は、条件分岐のロジックを「データ」として管理するデータ駆動型 (Data-Driven) の考え方を取り入れて、条件分岐の可読性を上げるアプローチをご紹介します。

if文を減らしたい課題に直面した

エンスポーツでは、ユーザーの皆様全員がもっと充実したプロフィール情報を記載していただけるように、いろいろな工夫を導入している最中です。

その一環として、エンスポーツの「マイページ」に、現在のプロフィール状態に応じたNoticeを表示する改良を進めています。

たとえば写真の枚数、自己紹介文の文字数、登録したプロフィール項目数などの条件に応じて、その方が素敵な出会いをするために必要なアプローチを順番に表示する機能です。

ただ条件分岐をif文で延々と書いていくと、思わぬエラーが起きやすく可読性が落ちます。今回実装するケースだと7つの条件分岐があり、今後アプリがリッチになるにつれ、もっともっと分岐が増える可能性もあります。

そこでif文を重ねる以外の解決方法を考えることにしました。

if文で冗長なコードを書くデメリット

ユーザーのプロフィール情報や会員プラン、本人確認状況など、複数の条件に応じてマイページに表示するコンテンツを切り替えるケースを考えてみます。

従来の if文を使った実装では、次のようになりがちです。

Widget buildMyPageContent(User user) {
  if (user.isPremium && !user.isVerified) {
    return PremiumUserUnverifiedContent();
  } else if (user.isPremium && user.isVerified) {
    return PremiumUserContent();
  } else if (!user.isPremium && user.isVerified) {
    return FreeUserVerifiedContent();
  } else if (user.profileCompletion < 50) {
    return IncompleteProfileContent();
  } else {
    return DefaultContent();
  }
  // さらに条件が増えると、この下に延々とelse ifが続く…
}

このコードには、以下のような問題点があります。

  • 可読性の低下: 条件が増えるほど、コードが長くなり全体像を把握しにくくなります。
  • 保守性の低下: 新しい条件を追加したり、条件の優先順位を変更したりする際に、多くのコードを修正する必要があり、バグの温床になりやすいです。

条件が増えるにつれ、どんどんデメリットが大きくなる書き方です。

if文を使わずデータ駆動型のアプローチで解決する

この問題を解決するために、「条件」と「条件が満たされたときに表示するUI」をセットで管理するクラスを導入します。

ステップ1: 条件とUIを定義するクラスを作成する

まず、条件を判定するロジックと、それに対応するUIコンポーネント(ここではタイトルとコンテンツウィジェット)を保持するクラスを作成します。

// 表示するコンテンツの情報をまとめたクラス
class DisplayContent {
  final String title;
  final Widget content;

  DisplayContent({required this.title, required this.content});
}

// 条件と、条件が満たされた場合に返すコンテンツを定義するクラス
class ContentCondition {
  // 条件を判定する関数
  final bool Function(User user) condition;
  // 条件が満たされた場合に表示するコンテンツ
  final DisplayContent displayContent;

  ContentCondition({required this.condition, required this.displayContent});
}

ステップ2: 表示ルールのリストを作成する

次に、上で定義したContentConditionクラスを使って、表示したい条件のリストを作成します。このリストの順番が、そのまま条件判定の優先順位になります。

// ユーザー情報に基づいて表示すべきコンテンツの条件リスト
List<ContentCondition> getContentConditions(User user) {
  return [
    ContentCondition(
      condition: (user) => user.isPremium && !user.isVerified,
      displayContent: DisplayContent(title: "重要", content: PremiumUserUnverifiedContent()),
    ),
    ContentCondition(
      condition: (user) => user.isPremium && user.isVerified,
      displayContent: DisplayContent(title: "プレミアム会員様へ", content: PremiumUserContent()),
    ),
    ContentCondition(
      condition: (user) => !user.isPremium && user.isVerified,
      displayContent: DisplayContent(title: "お知らせ", content: FreeUserVerifiedContent()),
    ),
    ContentCondition(
      condition: (user) => user.profileCompletion < 50,
      displayContent: DisplayContent(title: "プロフィール", content: IncompleteProfileContent()),
    ),
    // ... 新しい条件はここに追加するだけ
  ];
}

ステップ3: ループ処理で最適なUIを決定し、描画する

最後に、UIを構築する側では、このリストを上から順にループ処理し、最初に見つかった条件に合致するコンテンツを表示します。

Widget buildMyPageContent(User user) {
  final conditions = getContentConditions(user);

  // 条件リストを上から順番にチェック
  for (final item in conditions) {
    if (item.condition(user)) {
      // 条件に一致したら、対応するWidgetを返して処理を終了
      return item.displayContent.content;
    }
  }

  // どの条件にも当てはまらなければ、デフォルトのWidgetを返す
  return DefaultContent();
}

呼び出し側のコードが非常にシンプルになりました。if文の連鎖がなくなり、行っているのは「条件リストの中から最適なものを探して表示する」という、ただ一つの責務だけです。

データ駆動型アプローチでif文を減らすメリット

可読性と保守性の向上

条件とそれに対応するUIが一つのまとまりとして定義されているため、誰が見ても理解しやすくなります。新しい表示パターンを追加したい場合も、リストに新しいContentConditionインスタンスを追加するだけで完了し、既存のロジックを破壊する心配がありません。

拡張性の高さ

条件の優先順位を変えたい場合は、リストの要素を入れ替えるだけです。これにより、仕様変更にも迅速かつ安全に対応できます。

幅広い応用

今回はマイページの新機能で起きた問題ですが、例えば以下のような様々な場面で応用できます。

  • ユーザーの本人確認状況に応じたチャット画面の表示切り替え
  • ユーザーの契約プランに応じた機能の有効/無効の制御
  • ユーザー属性(年齢、性別など)に応じたレコメンドコンテンツの表示

まとめ

この方法は、コードの可読性や保守性を劇的に改善しますが、パフォーマンスが飛躍的に向上するわけではありません。

しかしUIの条件分岐で扱う項目が数十〜百件程度であれば、if文やswitch文と比較してパフォーマンスに大きな差は生まれないため、ほとんどのケースで問題になることはないでしょう。

複雑な条件分岐は、if文を並べるのではなく、条件とロジックを「データ」として分離することで、シンプルでメンテナンスしやすいコードに生まれ変わります。

もしif文で冗長になったコードに困っているようでしたら、一度試してみてください。

カジュアルにお話しませんか?

エンスポーツでは、一緒にはたらく仲間を募集しています。

アプリ開発に携わっていただけるエンジニアはもちろん、広告集客・ライティングが得意なディレクターやマーケターの方も歓迎しています。

ご興味をもっていただけましたら、ぜひ気軽にご連絡ください。

Yuto Mori

記事を書いた人

Yuto Mori

Engineer

エンスポーツでフルスタックエンジニアとして働いています。趣味は3Dモデリング。学生時代はスポーツチャンバラで日本3位になりました。