case를 효과적으로 다루는 방법
시간이 난 김에 정리를 해본다.
개발자라면 거의 매일 마주하게 되는 고민......바로 case를 분기하는 시점을 어떻게 다룰것이냐 하는 것.
가장 베이직한 if/else도 있고, when, switch 등등......
오늘은 이걸 효과적으로 다루는 방법 중 하나인 다형성에 대해서 정리해봤다.
Avoid Using “when” Expression as Much as Possible. Use Polymorphism Instead
A tip for Android developers
betterprogramming.pub
윗 글 내용을 내가 쓴것처럼..막 써보겠다.
아래와 같은 케이스를 가정해보자
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
}
}
이런 케이스의 경우, dp라는 단위가 선언시에 계속 생성되며,
이미 뷰모델이 어떤 버튼을 사용할지 알고 있는데도.... 추가 런타임이 발생하게 된다.
따라서 이걸 해결하기 위해서는, 아래와 같이
/*
* 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) }
)
}
이렇게 다형성을 이용해,
하나의 ButtonSize 클래스를 크기가 다른 버튼들에서 사용할 수 있게 하고,
getHeightDp를이용해 크기만 리턴하게 해주면 된다. 굿.
아래는 조금 더 복잡한 예문이다.
1) 다형성을 사용해, 크기와 색, 텍스트가 다른 버튼을 만들었을 때.
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))
}
)
}
요것만 보면 위의 코드랑 크게 차이 없어보이지만....
2) when 표현식을 이용해 크기와 색, 텍스트가 다른 버튼을 만들었을 때
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
}
}
훨씬 길어짐.
그리고 2의 경우에는 나중에 다른 사이즈의 버튼을 추가해야할때...(기능을 확장할 때)
텍스트, 색, 크기 변환 함수에 각각 하나의 케이스를 추가해야하지만, 1의 경우는 코드 한 줄이면 끝난다.