0Pricing
Kotlin Academy · บทเรียน

สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด

สร้าง DSL สำหรับการกำหนดค่าอย่างง่ายด้วยรูปแบบ builder และ lambda ส่วนขยาย

สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

เป้าหมาย: DSL ที่ใช้งานได้จริง

เราจะสร้าง DSL ตัวสร้าง HTML ที่มีขนาดเล็กแต่สมจริงใน Kotlin รูปแบบเดียวกันนี้ใช้ได้กับตัวสร้างการกำหนดค่า ชุดข้อมูลสำหรับการทดสอบ หรือรายงาน ผลลัพธ์คือ API ที่ปลอดภัยตามชนิดข้อมูล รองรับสภาพแวดล้อมการพัฒนาได้ดี และใช้แทนการต่อสตริง

กำหนดคลาสพื้นฐานของ Tag

องค์ประกอบ HTML ทั้งหมดมีโครงสร้างร่วมกัน ได้แก่ ชื่อ แอตทริบิวต์ และองค์ประกอบลูก เริ่มต้นด้วยคลาสพื้นฐาน:

open class Tag(val name: String) {
    val attrs = mutableMapOf<String, String>()
    val children = mutableListOf<Tag>()
    fun render(indent: Int = 0): String {
        val pad = " ".repeat(indent)
        val attrStr = if (attrs.isEmpty()) "" else " " + attrs.entries.joinToString(" ") { "${it.key}=\"${it.value}\"" }
        val body = children.joinToString("\n") { it.render(indent + 2) }
        return "$pad<$name$attrStr>\n$body\n$pad</$name>"
    }
}

ใช้ @DslMarker

กำหนดแอนโนเทชันให้คลาสตัวสร้างทั้งหมด เพื่อไม่ให้ขอบเขตภายในเรียกเมธอดจากขอบเขตภายนอกโดยไม่ตั้งใจ:

@DslMarker
annotation class HtmlDsl

@HtmlDsl class Html : Tag("html")
@HtmlDsl class Head : Tag("head")
@HtmlDsl class Body : Tag("body")
@HtmlDsl class Div  : Tag("div")
@HtmlDsl class P   : Tag("p")

เพิ่มฟังก์ชันตัวสร้าง

คลาสแท็กแต่ละคลาสจะได้รับฟังก์ชันส่วนขยายที่สร้างแท็กลูก ใช้บล็อก และเพิ่มแท็กลูกนั้นลงในองค์ประกอบลูก:

fun Html.head(block: Head.() -> Unit) = Head().also { it.block(); children += it }
fun Html.body(block: Body.() -> Unit) = Body().also { it.block(); children += it }
fun Body.div(block: Div.() -> Unit) = Div().also { it.block(); children += it }
fun Div.p(text: String) = P().also { it.attrs["text"] = text; children += it }

ตัวสร้าง html ระดับบนสุด

จัดเตรียมจุดเริ่มต้นระดับบนสุดที่สร้างองค์ประกอบรากและใช้บล็อกของผู้ใช้:

fun html(block: Html.() -> Unit): Html {
    val root = Html()
    root.block()
    return root
}

การใช้ DSL

ผลลัพธ์คือ DSL ที่สะอาดและอ่านง่าย:

val page = html {
    head { }
    body {
        div {
            p("Hello, DSL!")
        }
    }
}
println(page.render())

การเพิ่มแอตทริบิวต์

เพิ่มตัวช่วย attr เพื่อตั้งค่าแอตทริบิวต์ตามอำเภอใจได้อย่างต่อเนื่อง:

fun Tag.attr(key: String, value: String) { attrs[key] = value }

// Usage
div {
    attr("class", "container")
    p("Content")
}

รูปแบบ DSL สำหรับการกำหนดค่า

รูปแบบเดียวกันนี้ใช้ได้กับออบเจ็กต์การกำหนดค่า แทนที่จะใช้แท็ก HTML ให้ใช้คลาสเฉพาะโดเมน:

class ServerConfig {
    var host = "localhost"
    var port = 8080
    var db: DbConfig? = null
    fun database(block: DbConfig.() -> Unit) { db = DbConfig().apply(block) }
}
fun server(block: ServerConfig.() -> Unit) = ServerConfig().apply(block)

การซ้อนและการประกอบ

เนื่องจากฟังก์ชันตัวสร้างแต่ละฟังก์ชันคืนค่าออบเจ็กต์ที่สร้างขึ้น คุณจึงสามารถประกอบตัวสร้างได้อย่างอิสระ ระดับความลึกของการซ้อน DSL จะตรงกับระดับความลึกของลำดับชั้นข้อมูล

การป้องกันข้อผิดพลาดด้วยชนิดข้อมูล

ระบบชนิดข้อมูลจะบังคับใช้โครงสร้าง คุณไม่สามารถเรียกใช้ body { } ภายใน Head ได้ เพราะ Head ไม่มีฟังก์ชันส่วนขยาย body โครงสร้างที่ไม่ถูกต้องจะทำให้เกิดข้อผิดพลาดขณะคอมไพล์

การเรนเดอร์และการทดสอบ

เนื่องจาก DSL สร้างต้นไม้ออบเจ็กต์ Kotlin ธรรมดา การเรนเดอร์และการทดสอบจึงทำได้ง่าย เพียงเรียกใช้ render() แล้วตรวจสอบสตริงผลลัพธ์ หรือเดินสำรวจต้นไม้ออบเจ็กต์ในการทดสอบ

การขยาย DSL ในภายหลัง

การเพิ่มแท็กใหม่หรือตัวเลือกการกำหนดค่าใหม่ต้องใช้เพียงฟังก์ชันส่วนขยายใหม่บนคลาสตัวรับที่เหมาะสมเท่านั้น จุดเรียกใช้เดิมไม่ได้รับผลกระทบ ซึ่งแสดงหลักการเปิด/ปิดได้ในทางปฏิบัติ

ตรวจสอบอย่างรวดเร็ว

อะไรทำให้ DSL ของ Kotlin สำหรับ HTML/การกำหนดค่ามีความปลอดภัยด้านชนิดข้อมูล

สรุปทบทวน: การสร้าง DSL ที่ปลอดภัยด้านชนิดข้อมูล

ขั้นตอนสำคัญ:

  • กำหนดคลาสตัวสร้างสำหรับแต่ละระดับของลำดับชั้น
  • ใส่คำอธิบายประกอบด้วย @DslMarker เพื่อป้องกันการรั่วไหลของตัวรับ
  • เขียนฟังก์ชันส่วนขยายที่สร้าง กำหนดค่า และเพิ่มออบเจ็กต์ลูก
  • เปิดเผยฟังก์ชันจุดเริ่มต้นระดับบนสุด
  • การแสดงผลและการทดสอบทำงานกับออบเจ็กต์ Kotlin ทั่วไป

คำถามที่พบบ่อย

บทเรียน “สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด”

สร้าง DSL สำหรับการกำหนดค่าอย่างง่ายด้วยรูปแบบ builder และ lambda ส่วนขยาย คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม

ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Lambda พร้อมตัวรับ: รากฐานของ DSL
  2. @DslMarker: ป้องกันการรั่วไหลของตัวรับ
  3. สร้าง DSL สำหรับ HTML/การกำหนดค่าที่ปลอดภัยด้านชนิด
  4. ทดสอบและพัฒนา DSL โดยไม่ทำลายผู้ใช้
← กลับไปที่ Kotlin Academy