ViewModelとComposeの連携でMVVMパターンを実現【Kotlin入門⑪】

MVVMパターンとは?

MVVMの概要

MVVM(Model-View-ViewModel)パターンは、Androidアプリ開発においてデータとUIの分離を実現するための設計パターンです。このパターンを利用することで、アプリの保守性や拡張性が向上します。

MVVMは以下の3つの要素で構成されます:

  1. Model(モデル): アプリのデータやビジネスロジックを管理する。
  2. View(ビュー): UIを担当し、ユーザーの入力を受け取る。
  3. ViewModel(ビューモデル): ViewとModelの仲介役となり、UIの状態を管理する。

Jetpack ComposeとViewModelを組み合わせることで、MVVMパターンを簡潔に実装できます。

ViewModelの基本

ViewModelとは?

ViewModelは、画面のライフサイクルを超えてデータを保持し、UIの状態を管理するためのクラスです。Jetpack Composeでは、remembermutableStateOfを使ってUIの状態を管理することもできますが、よりスケーラブルなアプローチとしてViewModelを利用するのが一般的です。

ViewModelの実装

ViewModelはViewModelクラスを継承して作成します。

class CounterViewModel : ViewModel() {
    private var _count = mutableStateOf(0)
    val count: State<Int> = _count

    fun increment() {
        _count.value++
    }
}

このViewModelでは、mutableStateOfを使ってカウントの状態を管理し、increment()関数で値を更新します。

ComposeとViewModelの連携

ViewModelのインスタンス取得

Jetpack Composeでは、viewModel()を使ってViewModelのインスタンスを取得できます。

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "カウント: ${viewModel.count.value}", fontSize = 24.sp)
        Button(onClick = { viewModel.increment() }) {
            Text("増加")
        }
    }
}

このコードでは、viewModel()を使ってCounterViewModelのインスタンスを取得し、その状態をTextコンポーネントに表示しながら、Buttonをクリックすることで値を更新しています。

ViewModelとStateの最適な管理

Stateの管理方法

ComposeとViewModelを連携させる際には、状態を適切に管理することが重要です。LiveDataStateFlowを使用すると、状態の更新をより効率的に管理できます。

LiveDataの利用

class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}

Compose内でLiveDataを監視するには、observeAsState()を使用します。

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    val count by viewModel.count.observeAsState(0)
    
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "カウント: $count", fontSize = 24.sp)
        Button(onClick = { viewModel.increment() }) {
            Text("増加")
        }
    }
}

StateFlowの利用

StateFlowを使用する場合は、以下のように実装します。

class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count.asStateFlow()

    fun increment() {
        _count.value++
    }
}

Compose側では、collectAsState()を使って状態を監視します。

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    val count by viewModel.count.collectAsState()
    
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "カウント: $count", fontSize = 24.sp)
        Button(onClick = { viewModel.increment() }) {
            Text("増加")
        }
    }
}

ViewModelとリポジトリパターンの組み合わせ

リポジトリパターンとは?

リポジトリパターンを導入することで、データ取得の責務をViewModelから分離し、より保守しやすいアーキテクチャを実現できます。

class CounterRepository {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> get() = _count

    fun increment() {
        _count.value++
    }
}

ViewModelではリポジトリを利用して状態を管理します。

class CounterViewModel(private val repository: CounterRepository) : ViewModel() {
    val count: StateFlow<Int> = repository.count

    fun increment() {
        repository.increment()
    }
}

まとめ

  • MVVMパターンは、データとUIの分離を目的とした設計パターン。
  • ViewModelを利用することで、Composeの状態管理が容易に。
  • LiveDataやStateFlowを使うことで、より効率的な状態管理が可能。
  • リポジトリパターンを組み合わせると、より拡張性の高いアーキテクチャが構築できる。

Jetpack ComposeとViewModelを適切に組み合わせて、スケーラブルなアプリを開発しましょう!