Menu
CoddyTech

Pola Decorator

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Kotlin di Coddy. Pelajaran 47 dari 57.

Pola decorator menambahkan perilaku pada object dengan membungkusnya dalam object lain yang memiliki antarmuka yang sama. Decorator meneruskan panggilan ke object yang dibungkusnya dan menambahkan bagiannya sendiri, sehingga decorator dapat ditumpuk:

interface Coffee {
    fun cost(): Int
    fun description(): String
}
class Espresso : Coffee {
    override fun cost() = 200
    override fun description() = "espresso"
}
class Milk(private val inner: Coffee) : Coffee {
    override fun cost() = inner.cost() + 50
    override fun description() = inner.description() + ", milk"
}
class Syrup(private val inner: Coffee) : Coffee {
    override fun cost() = inner.cost() + 30
    override fun description() = inner.description() + ", syrup"
}

Di dalam main:

val order = Syrup(Milk(Milk(Espresso())))
println("${order.description()}: ${order.cost()}")

Output:

espresso, milk, milk, syrup: 330

Saat decorator mengubah hasil, urutannya penting: masing-masing bekerja pada hasil yang dikembalikan oleh object di dalamnya:

interface Text {
    fun render(): String
}
class Word(private val w: String) : Text {
    override fun render() = w
}
class Bracket(private val inner: Text) : Text {
    override fun render() = "[" + inner.render() + "]"
}
class Twice(private val inner: Text) : Text {
    override fun render() = inner.render() + inner.render()
}

Di dalam main:

println(Twice(Bracket(Word("hi"))).render())
println(Bracket(Twice(Word("hi"))).render())

Output:

[hi][hi]
[hihi]

Dengan by, decorator hanya menulis member yang diubahnya dan meneruskan sisanya secara otomatis:

interface Source {
    fun read(): String
    fun name(): String
}
class FileSource(private val content: String) : Source {
    override fun read() = content
    override fun name() = "notes.txt"
}
class Censored(private val inner: Source, private val word: String) : Source by inner {
    override fun read() = inner.read().replace(word, "***")
}

Di dalam main:

val src = Censored(FileSource("the secret plan"), "secret")
println("${src.name()}: ${src.read()}")

Output:

notes.txt: the *** plan

Dekorator digabungkan saat program berjalan, misalnya berdasarkan pilihan pelanggan, sedangkan subclass menetapkan satu kombinasi saat kode ditulis. Tiga tambahan sebagai subclass memerlukan satu class untuk setiap kombinasi. Sebagai dekorator, fold menyusunnya dari sebuah list:

interface Coffee {
    fun cost(): Int
}
class Espresso : Coffee {
    override fun cost() = 200
}
class Extra(private val inner: Coffee, private val price: Int) : Coffee {
    override fun cost() = inner.cost() + price
}

Di dalam main:

val prices = mapOf("milk" to 50, "syrup" to 30, "cream" to 70)
val choices = listOf("milk", "cream", "milk")
val coffee = choices.fold<String, Coffee>(Espresso()) { c, extra -> Extra(c, prices.getValue(extra)) }
println(coffee.cost())

Output:

370
challenge icon

Tantangan

Mudah

Pizza dibuat menggunakan decorator dari interface yang disediakan, Pizza (dengan price() dan toppings()). Tulis Base (800, tanpa topping) dan decorator Cheese (+150, menambahkan cheese), Olives (+100, menambahkan olives) dan DoubleUp, yang menggandakan harga semua yang ada di dalamnya dan mengulang toppingnya dua kali. Lalu tulis build(names), yang memulai dari Base dan membungkusnya dengan satu decorator untuk setiap nama, sesuai urutan (cheese, olives atau double).

Kode yang disediakan membaca baris berisi nama yang dipisahkan koma, atau plain, membuat setiap pizza dan mencetak 1050: cheese, olives atau 800: plain.

Tulis kodemu di Base.kt, Cheese.kt, Olives.kt, DoubleUp.kt, Build.kt dan Pizza.kt. Main.kt berisi kode input/output yang disediakan dan tidak dapat diedit.

Coba sendiri

fun main() {
    // Kode input/output yang disediakan: biarkan apa adanya
    val input = generateSequence(::readLine).toList()
    for (line in input) {
        val names = if (line == "plain") listOf() else line.split(",")
        val pizza = build(names)
        val toppings = pizza.toppings()
        println("${pizza.price()}: " + if (toppings.isEmpty()) "plain" else toppings.joinToString(", "))
    }
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Playground Kotlin