728x90
반응형
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
}
}
코드 중복과 관련된 두 가지 문제가 있습니다.
- Int 에서 반복적으로 creationDp 를 호출합니다.
- 추가적인 런타임 체크입니다. 우리는 이미 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 문 사용시>
다형성의 이점:
- 코드베이스를 더 작게 만듭니다.
- 기능을 확장하는 것이 더 쉽습니다.
- 코드 중복을 제거합니다.
- 런타임 오버헤드를 줄입니다.
728x90
반응형