web-dev-qa-db-fra.com

Instruction "When" vs Java "switch"

La correspondance de modèle dans Kotlin est Nice et le fait qu'elle n'exécute pas la correspondance de modèle suivante est bonne dans 90% des cas d'utilisation.

Sous Android, lorsque la base de données est mise à jour, nous utilisons la propriété de commutateur Java pour passer au cas suivant si nous ne mettons pas de pause pour que le code ressemble à cela:

switch (oldVersion) {
    case 1: upgradeFromV1();
    case 2: upgradeFromV2(); 
    case 3: upgradeFromV3();
}

Ainsi, si quelqu'un a une application avec la version 1 de la base de données et a manqué la version de l'application avec DB v2, il exécutera tout le code de mise à niveau nécessaire.

Convertis en Kotlin, nous obtenons un désordre comme:

when (oldVersion) {
    1 -> {
        upgradeFromV1()
        upgradeFromV2()
        upgradeFromV3()
    }
    2 -> {
        upgradeFromV2()
        upgradeFromV3()
    }
    3 -> {
        upgradeFromV3()
    }
}

Ici, nous n’avons que 3 versions, imaginez quand DB atteint la version 19: /

Quoi qu'il en soit à faire en agissant de la même manière que switch? J'ai essayé de continuer sans chance.

48
Geob-o-matic

La solution simple mais verbeuse est la suivante:

if (oldVersion <= 1) upgradeFromV1()
if (oldVersion <= 2) upgradeFromV2()
if (oldVersion <= 3) upgradeFromV3()

Une autre solution possible avec références de fonction :

fun upgradeFromV0() {}
fun upgradeFromV1() {}
fun upgradeFromV2() {}
fun upgradeFromV3() {}

val upgrades = arrayOf(::upgradeFromV0, ::upgradeFromV1, ::upgradeFromV2, ::upgradeFromV3)

fun upgradeFrom(oldVersion: Int) {
    for (i in oldVersion..upgrades.lastIndex) {
        upgrades[i]()
    }
}
59
bashor

edit: Réponse originale ci-dessous. Voici ce que je fais actuellement:

fun upgrade() {
    fun upgradeFromV1() { /* Do stuff */ }
    fun upgradeFromV3() { /* Do stuff */ }

    tailrec fun upgradeFrom(version: Int): Unit = when (version) {
        LATEST_VERSION -> {
            Config.version = version
        } 1 -> {
            upgradeFromV1()
            upgradeFrom(2)
        } in 2..3 -> {
            upgradeFromV3()
            upgradeFrom(4)
        } else -> {
            Log("Uncaught upgrade from $version")
            upgradeFrom(version+1)
    }

    upgradeFrom(Config.version)
}

Voici une variation de la réponse @ C.A.B. a donné:

fun upgrade(oldVersion: Int) {
    when (oldVersion) {
        latestVersion -> return
        1 -> upgradeFromV1()
        2 -> upgradeFromV2()
        3 -> upgradeFromV3()
    }
    upgrade(oldVersion + 1)
}
16
Julian Delphiki

Que dis-tu de ça:

fun upgradeFromV3() {/* some code */}
fun upgradeFromV2() {/* some code */ upgradeFromV3()}
fun upgradeFromV1() {/* some code */ upgradeFromV2()}
fun upgradeFromV0() {/* some code */ upgradeFromV1()}

fun upgrade(oldVersion: Int) {
    when (oldVersion) {
        1 -> upgradeFromV1()
        2 -> upgradeFromV2()
        3 -> upgradeFromV3()
    }
}

Ajoutée:

J'aime l'idée de @lukle de définir le chemin de mise à niveau sous forme de liste. Cela permet de définir différents chemins de mise à niveau pour différentes étapes initiales. Par exemple:

  1. Chemin rapide simple de la version publiée à la dernière version publiée
  2. Chemin de rattrapage de la version du correctif logiciel (il peut y avoir quelques rangs), à ne pas appliquer lors du passage de la version complète précédente à la version complète suivante

Pour cela, nous devons savoir à partir de quels éléments de la liste appliquer.

fun <Vs, V> Pair<Vs, V>.apply(upgrade: () -> Unit): (V) -> V {
    return { current: V ->
        if (first == current) {
            upgrade()
            second
        } else {
            current
        }
    }
}

val upgradePath = listOf(
        (0 to 10).apply  { /* do something */ },
        (5 to 15).apply  { /* do something */ },
        (10 to 20).apply { /* do something */ },
        (15 to 20).apply { /* do something */ },
        (20 to 30).apply { /* do something */ },
        (30 to 40).apply { /* do something */ }
)

fun upgrade(oldVersion: Int) {
    var current = oldVersion
    upgradePath.forEach { current = it(current) }
}

Dans ce code, Vs peut être identique à V ou à une sorte de collection de valeurs V avec une méthode equals(other: Any?): Boolean surchargée.

11
C.A.B.

C'est tout à fait possible Citation tirée de la référence officielle: Flux de contrôle: si, quand, pour, pendant

If many cases should be handled in the same way, the branch conditions may be combined with a comma:

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

Donc, si la même liste de conditions est courte, vous pouvez les lister en les séparant par des virgules ou utiliser des plages telles que la condition en 1..10, comme indiqué dans d'autres réponses. 

2
Yarh

Voici un mélange des deux réponses de Bashor, avec un peu de sucre fonctionnel:

fun upgradeFromV0() {}
fun upgradeFromV1() {}
fun upgradeFromV2() {}
fun upgradeFromV3() {}

val upgrades = arrayOf(::upgradeFromV0, ::upgradeFromV1, ::upgradeFromV2, ::upgradeFromV3)

fun upgradeFrom(oldVersion: Int) {
    upgrades.filterIndexed { index, kFunction0 -> oldVersion <= index }
            .forEach { it() }
}
0
lukle

Une autre variante de la réponse de OP:

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    when (oldVersion) {
        newVersion -> return
        1 -> TODO("upgrade from v1 to v2")
        2 -> TODO("upgrade from v2 to v3")
    }
    oldVersion++
    onUpgrade(db, oldVersion, newVersion)
}
0
arslancharyev31

Qu'en est-il de Kotlin DSL pour une implémentation personnalisée? Quelque chose comme cette approche:

class SwitchTest {

    @Test
    fun switchTest() {

        switch {
            case(true) {
                println("case 1")
            }
            case(true) {
                println("case 2")
            }
            case(false) {
                println("case 3")
            }
            caseBreak(true) {
                println("case 4")
            }
            case(true) {
                println("case 5")
            }
//          default { //TODO implement
//
//          }
        }
    }
}

class Switch {
    private var wasBroken: Boolean = false

    fun case(condition: Boolean = false, block: () -> Unit) {
        if (wasBroken) return
        if (condition)
            block()
    }

    fun caseBreak(condition: Boolean = false, block: () -> Unit) {
        if (condition) {
            block()
            wasBroken = true
        }
    }
}

fun switch(block: Switch.() -> Unit): Switch {
    val switch = Switch()
    switch.block()
    return switch
}

Il affiche: case 1 case 2 case 4 UPD: Quelques refactorings et exemple de sortie.

0
Victor Yevitchenko

Vous pouvez simplement utiliser pour la boucle avec quand.

for (version in oldVersion..newVersion) when (version) {
    1 -> upgradeFromV1()
    2 -> upgradeFromV2()
    3 -> upgradeFromV3()
}
0
John