팩토리 패턴
Coddy Kotlin 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 43번째.
팩토리 패턴은 어떤 클래스를 생성할지에 대한 결정을 한곳에 모읍니다. 호출자는 필요한 것을 설명하고 구체적인 클래스의 이름을 지정하지 않은 채 인터페이스 타입을 받습니다:
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
}
}main 내부에서:
println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))출력:
sms to ada
nullKotlin에서 팩토리는 인터페이스나 기본 클래스의 companion object에 있는 함수인 경우가 많으므로, 호출자는 Shape.from("circle 2")와 같이 작성합니다. 팩토리는 무언가를 생성하기 전에 입력을 구문 분석하고 확인할 수 있습니다:
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
}main 내부에서:
println(Shape.from("square 3")?.area())
println(Shape.from("circle x")?.area())출력:
9.0
null호출자는 인터페이스만 보므로, 팩토리는 호출자 코드를 건드리지 않고 반환하는 클래스를 변경할 수 있습니다. 여기서는 작은 데이터는 메모리에 저장하고 큰 데이터는 디스크에 저장합니다:
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")main 내부에서:
println(storageFor(5).describe())
println(storageFor(500).describe())출력:
memory
disk at /tmp/cache팩토리는 값일 수도 있습니다. (String) -> Animal 타입의 함수는 호출자가 객체의 클래스를 알 필요 없이 객체를 생성합니다. 이름에서 이러한 함수로 매핑하는 것은 레지스트리이며, ::Dog와 같은 생성자 참조는 이미 만들어진 팩토리 함수입니다:
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"
}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()}")출력:
Tom says Meow챌린지
쉬움제공된 인터페이스 Exporter(extension 및 export(title, lines))를 여러 내보내기 클래스가 구현합니다. MarkdownExporter(확장자 md: # Title 줄 하나와 각 줄마다 하나의 - item 줄), CsvExporter(확장자 csv: title,Title 및 각 줄마다 line,item)와 PlainExporter(확장자 txt: 제목을 대문자로 표시한 뒤 줄을 변경 없이 표시)를 작성하세요. 그런 다음 팩토리 객체 ExporterFactory를 작성하세요. create(format)는 대소문자를 구분하지 않고 markdown, csv 또는 plain에 해당하는 내보내기 객체를 반환하거나 null을 반환하며, supported()는 형식 이름을 반환합니다.
제공된 코드는 형식, 제목 및 줄을 읽고 report.md와 내보낸 텍스트를 출력하거나 unsupported format pdf; try markdown, csv, plain을 출력합니다.
코드는 MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt 및 Exporter.kt에 작성하세요. Main.kt에는 제공된 입력/출력 코드가 들어 있으며 편집할 수 없습니다.
직접 해보기
fun main() {
// 제공된 입력/출력 코드: 그대로 유지하세요
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)))
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: Kotlin 플레이그라운드