Menu
CoddyTech

Pola Factory

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

Pola factory menempatkan keputusan tentang class mana yang akan dibuat di satu tempat. Pemanggil menjelaskan kebutuhannya dan menerima tipe interface, tanpa menyebut nama class konkret:

interface Notifier {
    fun send(to: String): String
}
class EmailNotifier : Notifier {
    override fun send(to: String) = "email to $to"
}
class SmsNotifier : Notifier {
    override fun send(to: String) = "sms to $to"
}

object NotifierFactory {
    fun create(channel: String): Notifier? = when (channel) {
        "email" -> EmailNotifier()
        "sms" -> SmsNotifier()
        else -> null
    }
}

Di dalam main:

println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))

Output:

sms to ada
null

Di Kotlin, factory sering berupa fungsi di companion object dari interface atau base class, sehingga pemanggil menulis Shape.from("circle 2"). Factory dapat mengurai dan memeriksa input sebelum membuat apa pun:

interface Shape {
    fun area(): Double

    companion object {
        fun from(text: String): Shape? {
            val p = text.split(" ")
            val n = p.getOrNull(1)?.toDoubleOrNull() ?: return null
            return when (p[0]) {
                "circle" -> Circle(n)
                "square" -> Square(n)
                else -> null
            }
        }
    }
}
class Circle(val r: Double) : Shape {
    override fun area() = 3.14 * r * r
}
class Square(val s: Double) : Shape {
    override fun area() = s * s
}

Di dalam main:

println(Shape.from("square 3")?.area())
println(Shape.from("circle x")?.area())

Output:

9.0
null

Pemanggil hanya melihat antarmuka, jadi factory dapat mengubah class yang dikembalikannya tanpa mengubah pemanggil. Di sini, data kecil disimpan di memori dan data besar disimpan ke disk:

interface Storage {
    fun describe(): String
}
class MemoryStorage : Storage {
    override fun describe() = "memory"
}
class DiskStorage(val path: String) : Storage {
    override fun describe() = "disk at $path"
}

fun storageFor(sizeMb: Int): Storage = if (sizeMb < 100) MemoryStorage() else DiskStorage("/tmp/cache")

Di dalam main:

println(storageFor(5).describe())
println(storageFor(500).describe())

Output:

memory
disk at /tmp/cache

Factory juga bisa berupa nilai: fungsi bertipe (String) -> Animal membuat object tanpa pemanggil mengetahui class-nya. Map dari nama ke fungsi semacam itu disebut registry, dan referensi konstruktor seperti ::Dog adalah fungsi factory siap pakai:

open class Animal(val name: String) {
    open fun sound() = "..."
}
class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof"
}
class Cat(name: String) : Animal(name) {
    override fun sound() = "Meow"
}

Di dalam main:

val makers: Map<String, (String) -> Animal> = mapOf("dog" to ::Dog, "cat" to ::Cat)
val pet = makers.getValue("cat")("Tom")
println("${pet.name} says ${pet.sound()}")

Output:

Tom says Meow
challenge icon

Tantangan

Mudah

Beberapa exporter mengimplementasikan interface Exporter yang disediakan (extension dan export(title, lines)). Tulis MarkdownExporter (extension md: satu baris # Title dan satu baris - item untuk setiap baris), CsvExporter (extension csv: title,Title, lalu line,item untuk setiap baris), dan PlainExporter (extension txt: judul dalam huruf kapital, lalu baris-baris tanpa perubahan). Kemudian tulis object factory ExporterFactory: create(format) mengembalikan exporter untuk markdown, csv, atau plain, tanpa membedakan huruf besar dan kecil, atau null, dan supported() mengembalikan nama format.

Kode yang disediakan membaca format, judul, dan baris-baris, lalu mencetak report.md dan teks yang diekspor, atau unsupported format pdf; try markdown, csv, plain.

Tulis kodemu di MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt, dan Exporter.kt. Main.kt berisi kode input/output yang disediakan dan tidak dapat diedit.

Coba sendiri

fun main() {
    // Kode input/output yang disediakan: biarkan tetap seperti ini
    val input = generateSequence(::readLine).toList()
    val exporter = ExporterFactory.create(input[0])
    if (exporter == null) {
        println("unsupported format ${input[0]}; try ${ExporterFactory.supported().joinToString(", ")}")
    } else {
        println("report.${exporter.extension}")
        println(exporter.export(input[1], input.drop(2)))
    }
}
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