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

- 開発

アプリ開発をしている中、とくに機能がリッチになるにつれて、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
Engineer