Много обнуляемых объектов, как установить свойство в конце пути - PullRequest
2 голосов
/ 22 января 2020

Давайте предположим, что мы должны установить свойство для объекта "ObjectD". Чтобы получить этот объект, нам нужно go пройти через обнуляемые объекты:

objectA.objectB?.objectC?.objectD?.property = 1234

Проблема в том, что каждый объект должен быть проверен, если он равен нулю, то должен быть создан.

Есть ли способ сделать это без операторов if?

if (objectA.objectB == null) {
    objectA.objectB = ObjectB().apply {
        objectC = ObjectC().apply {
            objectD = ObjectD().apply {property = 1444}
        }
    }
} else {
    if (objectA.objectB.objectC == null) {
        objectA.objectB.objectC = ObjectC().apply {
            objectD = ObjectD().apply {property = 144}
        }
    }
}

Ответы [ 4 ]

1 голос
/ 22 января 2020

Лично я не избегаю if или, возможно, троичного оператора (? :)

Но я обычно проверяю, чтобы все объекты, которые я буду использовать, были ненулевыми перед их использованием.

В вашем случае я бы избежал вложения if, написав:

val wasBNull = false
val wasCNull = false
if (objectA.objectB == null) {
   wasBNull = true
   objectA.objectB = ObjectB()
}
if (objectA.objectB.objectC == null) {
   wasCNull = true
   objectA.objectB.objectC = ObjectC()
}

// ...
1 голос
/ 22 января 2020

Для этого, я думаю, вы должны создать метод (ы), например getOrCreateObjectB(): ObjectB. Если вы можете изменить внутреннее содержимое этих объектов - сделайте этот метод (методы) функцией-членом класса, если нет - расширением. Пример расширения приведен ниже:

fun ObjectA.getOrCreateObjectB(): ObjectB {
    if (objectB == null) objectB = ObjectB()
    return objectB
}

В конечном итоге вы получите что-то вроде этого: objectA.getOrCreateObjectB().getOrCreateObjectC().getOrCreateObjectD().property = 1234

0 голосов
/ 22 января 2020

Если объекты все равно должны существовать на этом этапе, почему бы не сделать их внутренне ленивыми?

class ObjectA {
    val objectB by lazy (::ObjectB)
}

class ObjectB {
    val objectC by lazy (::ObjectC)
}

class ObjectC {
    val objectD by lazy (::ObjectD)
}

class ObjectD {
    var property: Int? = null
}


fun main(args: Array<String>) {
    val objectA = ObjectA()
    objectA.objectB.objectC.objectD.property = 42
}

Намного чище!

0 голосов
/ 22 января 2020

Необходимость непосредственного изменения глубоко вложенного объекта является признаком фундаментальной проблемы проектирования (отсутствие инкапсуляции). За исключением, может быть, если ваш объектный граф является моделью некоторой неумной структуры данных, такой как JSON или XML.

По крайней мере, вы должны разбить все создание внешнего объекта. У каждого объекта в дереве может быть функция, обеспечивающая получение некоторого возвращаемого значения, например:

class ObjectA {
    var objectB: ObjectB? = null

    fun requireObjectB() = objectB ?: ObjectB().also { objectB = this }
}

Это уже улучшило бы то, что у вас есть. Теперь вы можете сделать это:

objectA.requireObjectB().requireObjectC().requireObjectD().property = 1234

Для лучшей инкапсуляции каждый объект в дереве должен иметь функцию publi c для выполнения того, что вы делаете, поэтому вам не нужно вызывать глубоко в цепь. Вызывающий класс не должен иметь глубоких знаний о тонкостях дерева объектов с пятью глубинами. Любое незначительное изменение класса в середине дерева может взорвать ваш проект везде, где что-то пытается изменить что-то глубоко в дереве. Но если у вас есть функции publi c на всех этих классах, вы можете гарантировать, что изменения безопасны для выполнения, пока функции publi c все еще выполняют то, что от них ожидается.

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