Menu
Coddy logo textTech

Pattern Strategy

Fa parte della sezione Programmazione orientata agli oggetti del percorso Kotlin di Coddy. Lezione 45 di 57.

Il pattern strategy inserisce ogni variante di un algoritmo in una propria classe dietro un'interfaccia condivisa. Il codice che lo usa contiene una strategia e può sostituirla mentre il programma è in esecuzione:

interface Shipping {
    fun cost(kg: Int): Int
}
class Standard : Shipping {
    override fun cost(kg: Int) = 5 + kg
}
class Express : Shipping {
    override fun cost(kg: Int) = 15 + kg * 2
}

class Checkout(var shipping: Shipping) {
    fun total(price: Int, kg: Int) = price + shipping.cost(kg)
}

All'interno di main:

val checkout = Checkout(Standard())
println(checkout.total(100, 3))
checkout.shipping = Express()
println(checkout.total(100, 3))

Output:

108
121

Quando una strategia ha un solo metodo, è sufficiente un tipo funzione. La classe memorizza una funzione e la richiama:

class Formatter(var style: (String) -> String) {
    fun show(words: List<String>) = words.joinToString(" ") { style(it) }
}

All'interno di main:

val f = Formatter { it.uppercase() }
println(f.show(listOf("hello", "world")))
f.style = { it.reversed() }
println(f.show(listOf("hello", "world")))

Output:

HELLO WORLD
olleh dlrow

Una strategia sostituisce un when all'interno dell'algoritmo. Con when, ogni nuova opzione implica modificare l'algoritmo. Con le strategie, una nuova opzione è una nuova classe o lambda, e Checkout non cambia mai:

// senza una strategia: ogni nuova opzione richiede di modificare questa funzione
fun cost(kind: String, kg: Int) = when (kind) {
    "standard" -> 5 + kg
    else -> 15 + kg * 2
}
// con una strategia: aggiungi una classe, lascia Checkout invariato
class Overnight : Shipping {
    override fun cost(kg: Int) = 40
}

Una strategia viene spesso scelta in base ai dati, come il piano di un cliente, usando una mappa o una piccola fabbrica:

val discounts: Map<String, (Int) -> Int> = mapOf(
    "gold" to { price -> price * 80 / 100 },
    "silver" to { price -> price * 90 / 100 }
)

fun finalPrice(plan: String, price: Int) = discounts[plan]?.invoke(price) ?: price

All'interno di main:

println(finalPrice("gold", 200))
println(finalPrice("silver", 200))
println(finalPrice("basic", 200))

Output:

160
180
200
challenge icon

Sfida

Facile

L’attacco di un eroe è una strategia. L’interfaccia Attack (con name e damage(strength)) è fornita. Scrivi Sword (danno pari alla forza moltiplicata per 2), Bow (forza più 5) e Magic(mana) (forza moltiplicata per 3 quando il mana è almeno 10, altrimenti 1). Hero(name, strength, attack) può cambiare il proprio attack in qualsiasi momento, e strike() restituisce Ada hits with sword for 20 senza controllare quale attacco abbia.

Il codice fornito legge name,strength e poi i comandi equip bow, equip magic 15, equip sword oppure strike, e stampa il risultato di ogni colpo.

Il tuo codice va in Sword.kt, Bow.kt, Magic.kt, Hero.kt e Attack.kt. Main.kt contiene il codice fornito per l’input/output e non può essere modificato.

Provalo tu

fun main() {
    // Codice di input/output fornito: lascialo così com'è
    val input = generateSequence(::readLine).toList()
    val (name, strength) = input[0].split(",")
    val hero = Hero(name, strength.toInt(), Sword())
    for (cmd in input.drop(1)) {
        val p = cmd.split(" ")
        if (p[0] == "strike") {
            println(hero.strike())
            continue
        }
        hero.attack = when (p[1]) {
            "bow" -> Bow()
            "magic" -> Magic(p[2].toInt())
            else -> Sword()
        }
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Playground Kotlin