― それは“分割不足”ではなく“設計の歪み”かもしれない ―
※ChatGPTを使用して記事を作成しています。
Composeを書いていると、ある瞬間にこうなります。
@Composable
fun UserProfile(
name: String,
age: Int,
isFollowing: Boolean,
isLoading: Boolean,
errorMessage: String?,
onFollowClick: () -> Unit,
onRetryClick: () -> Unit,
onBackClick: () -> Unit
)
そしてこう思います。
「引数、多すぎないか?」
この違和感は正しいです。
そして多くの場合、それは単なる見た目の問題ではありません。
設計が崩れ始めているサインです。
この記事では、
- なぜ引数が増えるのか
- 何が問題なのか
- どう設計すればいいのか
を整理します。
なぜ引数は増えるのか
原因はシンプルです。
UIが複雑になったから
ですが、本質はもう一段深いです。
本当の原因
- 状態が増えた
- イベントが増えた
- 責務が増えた
つまり、
1つのComposableに役割を詰め込みすぎている
状態です。
■ 引数が増えると何が起きるか
① 可読性が崩壊する
呼び出し側:
UserProfile(
name = user.name,
age = user.age,
isFollowing = state.isFollowing,
isLoading = state.isLoading,
errorMessage = state.error,
onFollowClick = { ... },
onRetryClick = { ... },
onBackClick = { ... }
)
何をしているUIなのか、直感的に分かりません。
② 状態の一貫性が崩れる
isLoading = true
errorMessage = "Error"
この状態は正しいのか?
👉 矛盾の温床になります
(第8回の正規化問題に逆戻り)
③ Previewが辛くなる
@Preview
fun Preview() {
UserProfile(
name = "",
age = 0,
isFollowing = false,
...
)
}
👉 状態を作るのが苦痛
④ 再利用できなくなる
引数が多い = 依存が多い
👉 他の画面で使えない
解決策①:UI Stateにまとめる
まずやるべきことはこれです。
data class UserProfileUiState(
val name: String,
val age: Int,
val isFollowing: Boolean,
val isLoading: Boolean,
val errorMessage: String?
)
@Composable
fun UserProfile(
state: UserProfileUiState,
onFollowClick: () -> Unit,
onRetryClick: () -> Unit,
onBackClick: () -> Unit
)
メリット
- 引数が整理される
- 状態の意味が明確になる
- Previewしやすい
しかし、それでも増える場合
ここが重要です。
Stateにまとめても、まだ多い場合があります。
fun UserProfile(
state: UserProfileUiState,
onFollowClick: () -> Unit,
onRetryClick: () -> Unit,
onBackClick: () -> Unit,
onItemClick: (Item) -> Unit,
onDialogDismiss: () -> Unit
)
👉 まだ多い
この場合、問題は別です。
解決策②:Composableを分割する
引数が多い原因は、
責務が多いから
です。
例:分割前
- ヘッダー
- フォロー機能
- リスト
- ダイアログ
全部1つ
分割後
ProfileHeader(...)
FollowSection(...)
PostList(...)
ConfirmDialog(...)
これにより:
- 引数が局所化
- 責務が明確化
- 再利用可能
解決策③:イベントをまとめる
イベントが多い場合もあります。
onFollowClick
onRetryClick
onBackClick
onItemClick
これをまとめます。
方法:Eventクラス
sealed interface UserProfileEvent {
object FollowClick : UserProfileEvent
object RetryClick : UserProfileEvent
object BackClick : UserProfileEvent
data class ItemClick(val id: String) : UserProfileEvent
}
@Composable
fun UserProfile(
state: UserProfileUiState,
onEvent: (UserProfileEvent) -> Unit
)
メリット
- 引数削減
- イベント管理が一元化
- ViewModelと接続しやすい
解決策④:Slot APIを使う
構造的な複雑さの場合。
@Composable
fun ProfileLayout(
header: @Composable () -> Unit,
content: @Composable () -> Unit,
footer: @Composable () -> Unit
)
👉 柔軟性を確保
判断基準まとめ
引数が増えたときは、次を確認します。
パターン① 状態が多い
👉 Stateクラスにまとめる
パターン② 責務が多い
👉 Composableを分割する
パターン③ イベントが多い
👉 Eventにまとめる
パターン④ 構造が複雑
👉 Slot APIを使う
■ やってはいけない対処
❌ とりあえずそのまま
→ 技術的負債
❌ 引数を省略する(デフォルト値)
fun UserProfile(
isLoading: Boolean = false
)
→ 状態の意味が崩壊
❌ ViewModelを渡す
fun UserProfile(viewModel: VM)
→ 再利用・テスト・Preview全滅
■ まとめ
Composableの引数が増えるのは自然です。
しかしそれは、
設計を見直すべきタイミング
です。
本質
- 引数の数は問題ではない
- 責務の数が問題
解決アプローチ
- Stateにまとめる
- Composableを分割する
- Eventにまとめる
- Slotで構造を分離する
Composeは関数UIです。
だからこそ、
引数 = 設計そのもの
になります。
引数が増えたと感じたら、
それは単なるコードの問題ではなく、
👉 UI設計の再検討ポイント
です。

