نمط Factory
جزء من قسم البرمجة كائنية التوجه في رحلة Kotlin على Coddy. الدرس 43 من 57.
يضع نمط المصنع قرار الفئة التي يجب إنشاؤها في مكان واحد. يصف المستدعي ما يحتاج إليه ويتلقى نوع الواجهة، من دون تسمية فئة ملموسة:
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
nullفي Kotlin، تكون factory غالبًا دالةً في الكائن المرافق للواجهة أو للفئة الأساسية، لذا يكتب المستدعون 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)))
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1الفئات والكائنات
العمل مع الملفاتتعريف الفئاتالمنشئات الأساسيةكتل init والتحقق من الصحةالمنشئات الثانويةمراجعة - بطاقة المكتبة4الوراثة
الأصناف المفتوحة والأصناف الفرعيةتجاوز الأعضاءsuper والمنشئاتالأصناف المجرّدةمراجعة - دليل الموظفينتدرّب بنفسك: Playground لـ Kotlin