Mẫu Factory
Thuộc phần Lập trình hướng đối tượng trong hành trình Kotlin của Coddy. Bài học 43 trên 57.
Mẫu factory tập trung quyết định tạo lớp nào vào một nơi. Bên gọi mô tả nhu cầu và nhận về kiểu giao diện mà không cần nêu tên lớp cụ thể:
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
}
}Bên trong main:
println(NotifierFactory.create("sms")?.send("ada"))
println(NotifierFactory.create("fax")?.send("bo"))Đầu ra:
sms to ada
nullTrong Kotlin, factory thường là một hàm trong companion object của interface hoặc lớp cơ sở, để bên gọi viết Shape.from("circle 2"). Hàm này có thể phân tích và kiểm tra đầu vào trước khi tạo bất cứ thứ gì:
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
}Bên trong main:
println(Shape.from("square 3")?.area())
println(Shape.from("circle x")?.area())Đầu ra:
9.0
nullNgười gọi chỉ thấy giao diện, nên factory có thể thay đổi lớp mà nó trả về mà không cần chỉnh sửa chúng. Ở đây, dữ liệu nhỏ được lưu trong bộ nhớ, còn dữ liệu lớn được ghi vào đĩa:
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")Bên trong main:
println(storageFor(5).describe())
println(storageFor(500).describe())Đầu ra:
memory
disk at /tmp/cacheMột factory cũng có thể là một giá trị: hàm có kiểu (String) -> Animal tạo đối tượng mà không cần bên gọi biết lớp của chúng. Bản đồ ánh xạ tên đến các hàm như vậy là một registry, còn tham chiếu hàm khởi tạo như ::Dog là một hàm factory có sẵn:
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"
}Bên trong 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()}")Đầu ra:
Tom says MeowThử thách
DễMột số trình xuất triển khai giao diện đã cho Exporter (extension và export(title, lines)). Hãy viết MarkdownExporter (phần mở rộng md: một dòng # Title và mỗi dòng có một dòng - item), CsvExporter (phần mở rộng csv: title,Title, sau đó mỗi dòng có line,item) và PlainExporter (phần mở rộng txt: tiêu đề viết hoa, sau đó là các dòng giữ nguyên). Tiếp theo, hãy viết đối tượng nhà máy ExporterFactory: create(format) trả về trình xuất tương ứng với markdown, csv hoặc plain, không phân biệt chữ hoa chữ thường, hoặc trả về null; supported() trả về tên các định dạng.
Code được cung cấp đọc định dạng, tiêu đề và các dòng, rồi in ra report.md cùng văn bản đã xuất, hoặc unsupported format pdf; try markdown, csv, plain.
Code của bạn nằm trong các tệp MarkdownExporter.kt, CsvExporter.kt, PlainExporter.kt, ExporterFactory.kt và Exporter.kt. Main.kt chứa code đầu vào/đầu ra đã cho và bạn không thể chỉnh sửa tệp này.
Tự làm thử
fun main() {
// Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
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)))
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpHàm khởi tạo chínhKhối init và kiểm tra tính hợp lệHàm khởi tạo phụÔn tập: thẻ thư viện4Kế thừa
Lớp mở và lớp conGhi đè thành viênsuper và hàm khởi tạoLớp trừu tượngÔn tập: Danh bạ nhân viên2Thuộc tính và tính đóng gói
Trình truy cập tùy chỉnhBộ sửa đổi phạm vi truy cậpSetter riêng tưlazy và lateinitÔn tập: tài khoản ngân hàng5Giao diện
Khai báo giao diệnPhương thức mặc địnhNhiều giao diệnGiao diện như kiểu dữ liệuÔn tập: Phương thức thanh toánTự luyện tập: Playground Kotlin