寻求在Kotlin中设计干净数据类(模型)的建议

cczfrluj  于 7个月前  发布在  Kotlin
关注(0)|答案(1)|浏览(68)

我目前正在为一个AlarmTime示例设计一个整洁的数据类(模型),它将在Alarm类中扮演一个变量的角色。我提出了两个解决方案,我想知道哪一个更适合Kotlin约定,并且通常很适合这种情况。如果这两个想法都是错误的,我会非常感谢任何例子或指导你可以分享。
多谢了!

解决方案1

data class AlarmTime private constructor(
    val hour: Int,
    val minute: Int,
) {
    fun toCombinedMinutes(): Int = hour * 60 + minute

    companion object {
        private const val MAX_COMBINED_MINUTES = 1439 // 23 * 60 + 59

        operator fun invoke(hour: Int, minute: Int) =
            when {
                hour >= 24 || hour < 0 -> throw IllegalArgumentException(
                    "Invalid hour value. Hour must be within the range of 0 to 23.")
                minute >= 60 || minute < 0 -> throw IllegalArgumentException(
                    "Invalid minute value. Minute must be within the range of 0 to 59.")
                else -> AlarmTime(hour, minute)
            }

        operator fun invoke(combinedMinutes: Int) =
            when {
                combinedMinutes > MAX_COMBINED_MINUTES || combinedMinutes < 0 ->
                    throw IllegalArgumentException(
                        "Invalid combined minutes value. " +
                                "Combined minutes must be within the range of 0 to 1439"
                    )
                else -> AlarmTime(
                    hour = combinedMinutes / 60,
                    minute =  combinedMinutes % 60
                )
            }
    }
}

字符串

解决方案2

data class Hour private constructor (val value: Int) {

    companion object {
       operator fun invoke(hour: Int) =
           when {
               hour >= 24 || hour < 0 -> throw IllegalArgumentException(
                   "Invalid hour value. Hour must be within the range of 0 to 23."
               )
               else -> Hour(hour)
           }
    }
}

data class Minute private constructor(val value: Int) {

    companion object {
        operator fun invoke(minute: Int) =
            when {
                minute >= 60 || minute < 0 -> throw IllegalArgumentException(
                    "Invalid minute value. Minute must be within the range of 0 to 59."
                )
                else -> Minute(minute)
            }
    }
}

data class CombinedMinutes(val value: Int) {

    fun getHour(): Hour = Hour(this.value / 60)
    fun getMinute(): Minute = Minute(this.value % 60)

    companion object {
        private const val MAX_COMBINED_MINUTES = 1439 // 23 * 60 + 59

        operator fun invoke(combinedMinutes: Int) =
            when {
                combinedMinutes > MAX_COMBINED_MINUTES || combinedMinutes < 0 ->
                    throw IllegalArgumentException(
                        "Invalid combined minutes value. " +
                                "Combined minutes must be within the range of 0 to 1439"
                    )
                else -> CombinedMinutes(combinedMinutes)
            }

        operator fun invoke(hour: Hour, minute: Minute) =
            CombinedMinutes(hour.value * 60 + minute.value)
    }
}

data class AlamTime2 private constructor(val hour: Hour, val minute: Minute) {
    companion object {
        operator fun invoke(hour: Hour, minute: Minute) =
            AlamTime2(hour, minute)

        operator fun invoke(combinedMinutes: CombinedMinutes) =
            AlamTime2(combinedMinutes.getHour(), combinedMinutes.getMinute())
    }
}

e3bfsja2

e3bfsja21#

我想挑战你的前提,即数据类非常适合你打算做的事情。
虽然没有明确说明,但从代码中看,您似乎希望确保不能创建无效的AlarmTime。这对数据类不起作用,因为您免费获得的东西之一是复制函数,它强制执行值的类型,但不执行验证。特别是以下代码编译并运行:

AlarmTime(12,30).copy(minute = -1)
  .also { println(it) } // AlarmTime(hour=12, minute=-1)

字符串
这是解决方案1的问题,解决方案2更是如此-考虑将分钟设置为-1的示例:

AlamTime2(Hour(12), Minute(30))
  .copy(minute = Minute(1).copy(value = -1) )


如果没有特定的附加要求(例如equals/hashcode),我会选择解决方案1的简化变体

class AlarmTime(val hour: Int, val minute: Int) {

    constructor(combinedMinutes: Int) : this(
        hour = combinedMinutes / 60,
        minute = combinedMinutes % 60
    )
    
    init {
        require(hour in 0..23) {
            "Invalid hour value: $hour. Hour must be within the range [0 .. 23]"
        }
        require(minute in 0..59) {
            "Invalid minute value: $minute. Minute must be within the range [0 .. 59]."
        }
    }

    override fun toString(): String = "AlarmTime(hour=$hour, minute=$minute)"
}

相关问题