본문 바로가기

카테고리 없음

Avoid Using “when” Expression as Much as Possible. Use Polymorphism Instead

728x90
반응형

출처 : https://medium.com/better-programming/avoid-using-when-expression-as-much-as-possible-use-polymorphism-instead-890b92389060

When 문은 종종 smell code 로 간주되어 피해야만 합니다.

 
5개의 미리 정의된 크기로 개발해야 하는 Button 5개가 있다고 가정해 보겠습니다.
  • small (height = 16dp)
  • medium (height = 24dp)
  • large (height = 32dp)
  • huge (height = 40dp)
  • custom
 
 
sealed class ButtonSize {
    object Small : ButtonSize()
    object Medium : ButtonSize()
    object Large : ButtonSize()
    object Huge : ButtonSize()
    data class Custom(val heightDpInt: Int) : ButtonSize()
}

@Composable
fun RenderButton(
    buttonSize: ButtonSize,
    onButtonClick: () -> Unit,
    text: String
) {
    Button(
        modifier = Modifier.height(buttonSize.getButtonHeight()).fillMaxWidth(),
        onClick = onButtonClick,
        content = { Text(text = text) }
    )
}

private fun ButtonSize.getButtonHeight(): Dp {
    return when (this) {
        ButtonSize.Small -> 16.dp
        ButtonSize.Medium -> 24.dp
        ButtonSize.Large -> 32.dp
        ButtonSize.Huge -> 40.dp
        is ButtonSize.Custom -> this.heightDpInt.dp
    }
}
 

 

마지막 private 함수 getButtonHeight 를 자세히 살펴보고무엇이 잘못되었는지 이해합시다.
 

 

private fun ButtonSize.getButtonHeight(): Dp {
    return when (this) { // Useless checking type
        ButtonSize.Small -> 16.dp // code duplication
        ButtonSize.Medium -> 24.dp // code duplication
        ButtonSize.Large -> 32.dp // code duplication
        ButtonSize.Huge -> 40.dp // code duplication
        is ButtonSize.Custom -> this.heightDpInt.dp // code duplication
    }
}

코드 중복과 관련된 두 가지 문제가 있습니다.

 

  1. Int 에서 반복적으로 creationDp 를 호출합니다.
  2. 추가적인 런타임 체크입니다. 우리는 이미 ViewModel에서 어떤 버튼 높이를 사용해야 하는지를 결정했습니다. 왜 추가적으로 체크를 해야 하나요? 불필요합니다. 게다가 런타임 오버헤드가 발생합니다. 체크에 사용된 Android 휴대폰의 리소스는 애니메이션과 같은 다른 보다 가치 있는 작업에 사용될 수 있습니다.

 

class ViewModel(
    // ...
): ViewModel(){
  
  val buttonSizeLiveData = MutableLiveData<ButtonSize>()
  
  // ...
  
  private fun methodThatDoesSomething() {
    // ...
    buttonSizeLiveData.post(ButtonSize.Small) // Here we decided what size should be rendered
    // ...
  }
  // ...
}

private fun ButtonSize.getButtonHeight(): Dp {
    val heightInt = when (this) {
        ButtonSize.Small -> 16 // Code duplication! Type-checking should be avoided!
        ButtonSize.Medium -> 24
        ButtonSize.Large -> 32
        ButtonSize.Huge -> 40
        is ButtonSize.Custom -> this.heightDpInt
    }
    return heightInt.dp // Here we fixed the 1st code duplication problem
}
 

체크를 줄입시다. 해야 할 것은 when표현식을 다형성으로 바꾸는 것뿐입니다.

 

/*
 * Here we moved dp values from getButtonHeight method.
 * getButtonHeight method has been removed.
 */
sealed class ButtonSize(open val heightDpInt: Int) {
    object Small : ButtonSize(16)
    object Medium : ButtonSize(24)
    object Large : ButtonSize(32)
    object Huge : ButtonSize(40)
    data class Custom(override val heightDpInt: Int): ButtonSize(heightDpInt)
    
    fun getHeightDp(): Dp {
      return  heightDpInt.dp
    }
}

@Composable
fun RenderButton(
    buttonSize: ButtonSize,
    onButtonClick: () -> Unit,
    text: String
) {
    Button(
        modifier = Modifier.height(buttonSize.getHeightDp()).fillMaxWidth(),
        onClick = onButtonClick,
        content = { Text(text = text) }
    )
}
 

예제를 더 복잡하게 만들어 보겠습니다. 버튼의 텍스트와 색상이 버튼 크기에 따라 달라진다고 가정해 봅시다. 우리는 두 가지 경우를 모두 고려할 것입니다.
아래의 두 예제를 비교하세요.

 

sealed class ButtonSize(
    open val heightDpInt: Int,
    open val color: Color,
    open val textResId: Int
) {
    object Small : ButtonSize(16, Color.Red, R.string.small_button_text)
    object Medium : ButtonSize(24, Color.Gray, R.string.medium_button_text)
    object Large : ButtonSize(32, Color.Green, R.string.large_button_text)
    object Huge : ButtonSize(40, Color.Blue, R.string.huge_button_text)
    data class Custom(
        override val heightDpInt: Int,
        override val color: Color,
        override val textResId: Int
    ) : ButtonSize(heightDpInt, color, textResId)
    
    fun getHeightDp(): Dp {
        return heightDpInt.dp
    }
}

@Composable
fun RenderButton(
    buttonSize: ButtonSize,
    onButtonClick: () -> Unit
) {
    Button(
        modifier = Modifier
            .height(buttonSize.getHeightDp())
            .fillMaxWidth(),
        onClick = onButtonClick,
        colors = ButtonDefaults.buttonColors(
            backgroundColor = buttonSize.color,
        ),
        content = {
            Text(text = stringResource(buttonSize.textResId))
        }
    )
}

 <다형성 사용시>

 

sealed class ButtonSize() {
    object Small : ButtonSize()
    object Medium : ButtonSize()
    object Large : ButtonSize()
    object Huge : ButtonSize()
    data class Custom(
        val heightDpInt: Int,
        val color: Color,
        val textResId: Int
    ) : ButtonSize()
}

@Composable
fun RenderButton(
    buttonSize: ButtonSize,
    onButtonClick: () -> Unit
) {
    Button(
        modifier = Modifier
            .height(buttonSize.getButtonHeight())
            .fillMaxWidth(),
        onClick = onButtonClick,
        colors = ButtonDefaults.buttonColors(
            backgroundColor = buttonSize.getButtonColor(),
        ),
        content = {
            Text(text = stringResource(buttonSize.getButtonText()))
        }
    )
}

private fun ButtonSize.getButtonHeight(): Dp {
    return when (this) {
        ButtonSize.Small -> 16 // Code duplication
        ButtonSize.Medium -> 24 // Code duplication
        ButtonSize.Large -> 32 // Code duplication
        ButtonSize.Huge -> 40 // Code duplication
        is ButtonSize.Custom -> this.heightDpInt // Code duplication
    }
}

private fun ButtonSize.getButtonText(): Int {
    return when (this) {
        ButtonSize.Small -> R.string.small_button_text // Code duplication
        ButtonSize.Medium -> R.string.medium_button_text // Code duplication
        ButtonSize.Large -> R.string.large_button_text // Code duplication
        ButtonSize.Huge -> R.string.huge_button_text // Code duplication
        is ButtonSize.Custom -> this.textResId // Code duplication
    }
}

private fun ButtonSize.getButtonColor(): Color { 
  return when (this) {
        ButtonSize.Small -> Color.Red // Code duplication
        ButtonSize.Medium -> Color.Gray // Code duplication
        ButtonSize.Large -> Color.Green // Code duplication
        ButtonSize.Huge -> Color.Blue // Code duplication
        is ButtonSize.Custom -> this.color // Code duplication
    }
}

<when 문 사용시>

 

다형성의 이점:

  1. 코드베이스를 더 작게 만듭니다.
  2. 기능을 확장하는 것이 더 쉽습니다.
  3. 코드 중복을 제거합니다.
  4. 런타임 오버헤드를 줄입니다.
 
728x90
반응형