[Android] 상속으로 중복코드 없애기(1): Add,Edit의 UI 같이 사용하기

uuranus·2023년 9월 5일
0
post-thumbnail

Add와 Edit

  • 보통 게시물 추가처럼 어떤 걸 추가할 수 있는 기능을 만들게 되면 특별한 이유가 없다면 수정 기능도 만들게 된다.
  • 그럼 항상 이때 생기는 유혹이 Add와 Edit이 화면이 동일해서 ui에 관련된 코드는 같이 사용할 수 있는데.. -> edit에 관련된 메소드만 추가해서 같은 ViewModel을 사용해도 되지 않을까??
  • 그럼 ViewModel이 현재가 add인 상황인지 edit인 상황인지를 알아야 한다. -> isEdit이라는 변수를 만들어서 확인용으로 사용하자!
  • 이렇게 되니 edit인 경우 다르게 처리해주어야 하는 경우 if~else문이 추가되면서 코드가 복잡해짐
    • 예를 들어, 이미지의 경우 add일 때는 ByteArray로 처리했는데 edit일 때는 Uri로 처리한다면, showImage()같은 메서드가 edit일 때는 Uri로 보여주고 add일 때는 ByteArray로 보여주도록 if~else가 추가될 수 있다.
  • 공통적으로 가지는 ui의 데이터를 위한 변수들은 상위 클래스로 올리고 각자 필요한 변수와 메서드를 상속해서 가지고 있으면 어떨까 생각하여 직접 구현해보았다.

게시물 추가 화면과 수정 화면의 공통 ViewModel을 만들어보자

open class RecipeStepBottomViewModel : GoBongViewModel() {
    private val _thumbnailByteArray = MutableStateFlow(byteArrayOf())
    val thumbnailByteArray: StateFlow<ByteArray> get() = _thumbnailByteArray

    private val _minute = MutableStateFlow("0")
    val minute: StateFlow<String> get() = _minute

    private val _second = MutableStateFlow("0")
    val second: StateFlow<String> get() = _second

    val descriptionInputText = MutableStateFlow("")

    private val _tools = MutableStateFlow(
        listOf(
            Tool("전자레인지", false, true),
            Tool("에어프라이어", false, true),
            Tool("오븐", false, true),
            Tool("가스레인지", false, true),
            Tool("믹서", false, true),
            Tool("커피포트", false, true),
            Tool("프라이팬", false, true)
        )
    )
    val tools: StateFlow<List<Tool>> get() = _tools

    private val _isSavedSuccess = MutableStateFlow(false)
    val isSavedSuccess: StateFlow<Boolean> get() = _isSavedSuccess

    fun setSuccess(isSuccess: Boolean) {
        _isSavedSuccess.value = isSuccess
    }

    fun setThumbnailByteArray(byteArray: ByteArray) {
        _thumbnailByteArray.value = byteArray
    }

    fun setDescriptionText(text: String) {
        descriptionInputText.value = text
    }
}

ui state를 가지고 있는 변수와 그 변수를 set,get하는 함수들을 가지고 있다.

Add ViewModel

class RecipeStepAddBottomViewModel : RecipeStepBottomViewModel() {

    fun saveNewRecipeStep() {
        viewModelScope.launch {
            try {
                isSaveValidate()
                requestNewRecipe()
            } catch (e: Exception) {
                setToastMessage(e.message ?: "")
            }
        }
    }

    private suspend fun requestNewRecipe() {
        setSuccess(true)
    }

    fun getNewRecipeStep(): RecipeStepAdded {
        return RecipeStepAdded(
            "${minute.value}${second.value}초",
            tools.value.filter { it.isChecked }.map { it.toolName },
            thumbnailByteArray.value,
            descriptionInputText.value
        )
    }
}

새로운 recipe 단계를 추가하는 메서드를 가지고 있다.

Edit ViewModel

class RecipeStepEditBottomViewModel : RecipeStepBottomViewModel() {

    private val _isDeleted = MutableStateFlow(false)
    val isDeleted: StateFlow<Boolean> get() = _isDeleted

    private var editId = 0L

    fun getStepId() = editId

    fun setOldRecipe(recipe: RecipeStepAdded) {
        setThumbnailByteArray(recipe.photoUrl)
        checkTools(recipe.tools)
        val times = recipe.time.split(" ")
        addMinute(times[0].removeSuffix("분").toInt())
        addSecond(times[1].removeSuffix("초").toInt())
        setDescriptionText(recipe.description)
        editId = recipe.id
    }

    fun updateRecipeStep() {
        try {
            isSaveValidate()
            requestUpdatedRecipeStep()
        } catch (e: Exception) {
            setSuccess(false)
            setSnackBarMessage(e.message ?: "")
        }
    }

    private fun requestUpdatedRecipeStep() {
        setSuccess(true)
    }

edit의 경우 기존 데이터가 초반에 미리 세팅이 되어 있어야 함으로 setOldRecipe같은 메서드를 가진다.
또한, update나 delete가 되었는지, edit하고 있는 게시물의 Id 등등을 가지고 있다.

결론

  • edit화면에서 delete도 같이 하는 경우, edit일 때 필요한 변수, 메서드가 많아진다.
  • 이를 add할 때 코드와 같이 사용한다면 코드도 길어지고 보기 어려워질 것이다.
  • 공통된 부분은 상위 클래스로 올리고 add, edit 코드를 분리함으로써 코드가 깔끔해졌다.
profile
Frontend Developer

0개의 댓글