Реализация типобезопасной иерархии классов с нулевым значением - PullRequest
5 голосов
/ 04 июня 2019

У меня (часто) есть ресурс с двумя состояниями, предварительно созданными и созданными после, где оба состояния имеют одинаковые поля, за исключением поля id. id имеет значение NULL в предварительно созданном состоянии и не равно NULL в состоянии после создания.

Я хотел бы определить и использовать этот ресурс простым и безопасным для типов способом.

Распространено представлять это поле идентификатора как обнуляемое, которое обрабатывает оба сценария с минимальным образцом в определении класса. Проблема в том, что он создает много шаблонного элемента в бизнес-логике, потому что вы не можете утверждать, был ли ресурс предварительно создан или пост-создан, глядя на его тип.

Вот пример обнуляемого подхода:

data class Resource(val id: String?, val property: String)

Это просто определить, но не так просто обработать из-за отсутствия гарантий времени компиляции.

Вот пример более безопасного типа:

sealed class Resource(val property: String) {
  class WithoutID(property: String): Resource(property)
  class WithID(val id: String, property: String): Resource(property)
}

Это позволяет мне обойти Resource.WithID и Resource.WithoutID, которые имеют все те же поля и методы, за исключением id.

Единственное неудобство, связанное с этим безопасным типом подходом, заключается в том, что код определения ресурса становится довольно раздутым, когда у вас много полей property. Это вздутие живота делает код труднее для чтения.

Мне интересно, есть ли альтернативный подход с меньшим количеством шаблонов или есть ли у Kotlin какие-либо функции, которые делают подобные вещи проще.

Ответы [ 2 ]

1 голос
/ 05 июня 2019

Как насчет определения

sealed class MayHaveId<T> { abstract val record: T }
class WithId<T>(val id: String, override val record: T): MayHaveId<T>()
class WithoutId<T>(override val record: T): MayHaveId<T>()

class Resource(val property: String)
// and other similar types

и использование WithId<Resource> и WithoutId<Resource>? В Scala вы можете добавить неявное преобразование из MayHaveId<T> в T, но не в Kotlin, увы, и вы не можете написать : T by record. Все еще должен быть достаточно чистым, чтобы использовать.

0 голосов
/ 05 июня 2019

Один из вариантов - войти в композицию, опираясь на properties внутри интерфейсов.

interface Resource {
    val property: String
}

interface WithId : Resource {
    val id: Int
}

interface WithOtherField : Resource {
    val otherField: Any
}


class WithoutIdImpl(override val property: String) : Resource
class WithIdImpl(override val id: Int, override val property: String) : WithId
class WithIdAndOtherField(
        override val id: Int,
        override val otherField: Any,
        override val property: String) : WithId, WithOtherField

Я не понял из вашего примера, как вы собираетесь переключаться между двумя состояниями Resource.Так что, вероятно, есть пробел, который нужно преодолеть.Возможно, умные приведения позволят переключать состояния.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...