유스케이스는 비지니스로직을 수행하는 클래스입니다.
이 유스케이스는 하나의 기능을 하는 편이 좋습니다.
유스케이스가 하나의 기능만한다면, 어느 상황에서 어떤 유스케이스가 필요한지 바로바로 알 수 있습니다. 또한 수정에 용이합니다.
제 깃허브에 있는 메모앱을 예를들어 봅시다.
class memoUsecase @Inject constructor(private val repo : Repository) {
fun getAll() = repo.getAll()
fun createMemo(memo : MemoData) = repo.createMemo(memo)
fun deleteAllMemo() = repo.deleteAllMemo()
fun updateMemo(memo : MemoData) = repo.updateMemo(memo)
fun deleteMemo(memo : MemoData) = repo.deleteMemo(memo)
}
처음에 저는 유스케이스를 이렇게 구성했습니다.
이 유스케이스를 사용할때는 아래처럼 했습니다.
class CreateMemoViewModel @Inject constructor(
private val useCase: memoUsecase
) : AppViewModel() {
private fun createMemo(memo : MemoEntity) {
useCase.createMemo(memo)
.subscribeOnBackground()
.subscribeWithDisposable(this) {
_createMemoEvent.value = memo.toMemo()
}
}
private fun updateMemo(memo : MemoEntity){
useCase.updateMemo(memo)
.subscribeOnBackground()
.subscribeWithDisposable(this) {
_createMemoEvent.value = memo.toMemo()
}
}
//생략
class MemoViewModel @Inject constructor(
private val useCase: memoUsecase
) : AppViewModel(), MemoAdapterDelegate{
fun getAll() {
useCase.getAll()
.subscribeOnBackground()
.subscribeWithDisposable(this) {
_memoList.value = it.map { memo -> memo.toMemo() }
}
}
fun deleteMemo(memo : Memo){
useCase.deleteMemo(memo.toData())
.subscribeOnBackground()
.subscribeWithDisposable(this){
}
}
//생략
CreateMemoViewModel에서는 memoUsecase의 createMemo, updateMemo 함수 두 가지만 사용하고 있습니다.
나머지 함수는 CreateMemoViewModel에게는 필요없는 것입니다.
MemoViewModel에서는 getAll, deleteMemo 함수 두 가지만 사용합니다.
하나의 유스케이스를 각각 다른 목적으로 사용하고 있습니다.
이 상황에서 만약 memoUsecase를 수정할 일이 생겨서 getAll 함수를 수정했다면 어떨까요?
memoUsecase를 사용하는 모든 클래스가 제대로 돌아가는지 확신이 서지 않을 것입니다.
CreateMemoViewModel은 getAll 함수를 사용하지 않지만, 나중에 가서는 그 사실을 잊어버리기 쉽습니다.
따라서 CreateMemoViewModel도 수정의 여파를 받는 클래스에 염두에 두게됩니다.
수정이 프로그램에 미치는 영향은 최소화 해야합니다.
이 문제를 해결하기 위해서는 유스케이스를 최대한 작게 만드는 편이 좋습니다.
저는 유스케이스의 크기를 스코프라고 정의하고 싶습니다.
유스케이스의 스코프가 작으면 작을수록 이 유스케이스를 사용하는 경우의 수를 특정하기가 쉽고, 유지보수가 쉬워집니다.
위의 코드를 바꿔보겠습니다.
class CreateMemoUsecase @Inject constructor(private val repo : Repository) {
fun createMemo(memo : MemoData) = repo.createMemo(memo)
}
class DeleteMemoUsecase @Inject constructor(private val repo : Repository) {
fun deleteMemo(memo : MemoData) = repo.deleteMemo(memo)
}
class GetAllMemoUsecase @Inject constructor(private val repo : Repository) {
fun getAllMemo() = repo.getAll()
}
class UpdateMemoUsecase @Inject constructor(private val repo : Repository){
fun updateMemo(memo : MemoData) = repo.updateMemo(memo)
}
이렇게 유스케이스를 분리했습니다.
사용할 때는 아래처럼 하면됩니다.
class CreateMemoViewModel @Inject constructor(
private val createMemoUsecase: CreateMemoUsecase,
private val updateMemoUsecase: UpdateMemoUsecase
) : AppViewModel() {
private fun createMemo(memo : MemoData) {
createMemoUsecase.createMemo(memo)
.subscribeOnBackground()
.subscribeWithDisposable(this) {
_createMemoEvent.value = memo.toMemo()
}
}
private fun updateMemo(memo : MemoData){
updateMemoUsecase.updateMemo(memo)
.subscribeOnBackground()
.subscribeWithDisposable(this) {
_createMemoEvent.value = memo.toMemo()
}
}
class MemoViewModel @Inject constructor(
private val deleteMemoUsecase: DeleteMemoUsecase,
private val getAllMemoUsecase: GetAllMemoUsecase
) : AppViewModel(), MemoAdapterDelegate{
fun getAll() {
getAllMemoUsecase.getAllMemo()
.subscribeOnBackground()
.subscribeWithDisposable(this) {
_memoList.value = it.map { memo -> memo.toMemo() }
}
}
fun deleteMemo(memo : Memo){
deleteMemoUsecase.deleteMemo(memo.toData())
.subscribeOnBackground()
.subscribeWithDisposable(this){
}
}
유스케이스의 사용처가 보다 명확해졌음을 알 수 있습니다.
또한 수정도 용이해졌습니다. 만약 MemoViewModel에 더이상 메모를 삭제하는 기능이 필요없다고 하면 그저 deleMemoUsecase 를 사용하지 않으면 됩니다.
'안드로이드 앱개발' 카테고리의 다른 글
Permission (0) | 2021.09.04 |
---|---|
Dagger2 Scope (0) | 2021.09.04 |
안드로이드 앱개발에서의 클린아키텍쳐 (0) | 2021.04.17 |
java에서 kotlin으로 마이그레이션 & 리팩토링 (1) - asyncTask를 Coroutine 으로 대체하기 (0) | 2021.04.14 |
동영상 스플래시 스크린 띄우기 (동영상에 애니메이션 넣기) (0) | 2021.04.03 |