<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## DSL DSL(Domain-Specific Language,領域特定語言)指的是專注于特定領域問題的計算機語言,不同于通用的計算機語言(GPL),領域特定語言只用在某些特定的領域,這些領域可能是某一種產業,例如保險、教育、航空、醫療等,也可能是一種方法或技術,例如數據庫SQL、HTML等。本章將針對DSL進行詳細講解。 ### DSL簡介 #### DSL概述 DSL是問題解決方案模型的外部封裝,這個模型可能是一個API庫,也可能是一個完整的框架,沒有具體的標準來區分DSL和普通的API,DSL代碼更易于理解,不僅對于開發人員而且對于初學者來說也更易于理解。常見的DSL有軟件構建領域Ant、UI設計師HTML、硬件設計師VHDL。 DSL與通用的編程語言的區別如下。 * DSL供非程序員和領域專家使用。 * DSL有更高級的抽象,不涉及類似數據結構的細節。 * DSL表現力有限,其只能描述該領域的模型,而通用編程語言能夠描述任意的模型。 根據是否從宿主語言構建而來,DSL分為內部DSL(從一種宿主語言構建而來)和外部DSL(從零開始構建的語言,需要實現語法分析器等)。 在Kotlin中創建DSL時,一般有以下3個特性。 * 擴展函數與擴展屬性。 * 帶接收者的Lambda表達式(高階函數)。 * invoke()函數的調用約定(使用invoke約定可以構建靈活的代碼塊嵌套)。 #### DSL程序 上一個小節我們對DSL進行了簡單的介紹,接下來通過兩個案例來演示DSL風格的代碼與傳統代碼的區別。 1. 傳統風格的代碼 在IDEA中,創建一個名為Chapter11的Kotlin項目,該項目的包名指定為com.itheima. chapter11,接著在該包中創建一個DSL.kt文件,在該文件中按照傳統的形式分別創建一個Person類和Address類,具體代碼如下所示。 ``` class Person(var name: String? = null, var age: Int? = null, var address: Address? = null) { override fun toString(): String { return "Person(name='$name', age=$age, address=$address)" } } class Address(var city: String? = null, var street: String? = null, var number: Int? = null) { override fun toString(): String { return "Address(city='$city', street='$street', number=$number)" } } fun main(args: Array<String>) { val address = Address("北京", "長安街", 30) val person = Person("張三", 20, address) println(person) } ``` 運行結果: ``` Person(name='張三', age=20, address=Address(city='北京', street='長安街', number=30)) ``` 上述代碼中,創建了兩個類,分別是Person和Address,在Person類中傳遞了姓名、年齡以及住址信息。在Address類中傳遞了住址信息中的城市、街道以及門牌號。最后在main()函數中分別創建這兩個類的對象并打印person對象。 2. DSL風格的代碼 由于上面示例中的main()方法中的內容符合API的規范,但不符合人類的自然語言理解,如果想要讓初學者更好地理解代碼,則需要對這段代碼進行DSL風格的一個抽取。修改后的代碼如下所示。 ``` class Person(var name: String? = null, var age: Int? = null, var address: Address? = null) { override fun toString(): String { return "Person(name='$name', age=$age, address=$address)" } } class Address(var city: String? = null, var street: String? = null, var number: Int? = null) { override fun toString(): String { return "Address(city='$city', street='$street', number=$number)" } } /** * address()是一個擴展函數 * 這個函數中傳遞的參數也是一個函數,該函數的返回值為Unit */ fun Person.address(block: Address.() -> Unit) { val a = Address() address = a block(a) } /** * person()是一個擴展函數,這個函數返回一個Person對象, * 這個函數中傳遞的參數也是一個函數,該函數的返回值為Unit */ fun person(block: Person.() -> Unit): Person { //Person相當于把這個函數定義在Person中 val p = Person() block(p) //通過這個函數把main()函數中設置的對應的值傳遞過來 return p } fun main(args: Array<String>) { val p = person { //擴展函數 name = "張三" age = 20 address { city = "北京" street = "長安街" number = 30 } } println(p) } ``` 運行結果: ``` Person(name='張三', age=20, address=Address(city='北京', street='長安街', number=30)) ``` 上述代碼中,分別創建了person()擴展函數和address()擴展函數,這兩個擴展函數中通過block()函數將main()函數中設置的對應數據傳遞過來。在main()函數中,創建了一個Person的擴展函數,這個擴展函數既是一個lambda表達式,也是一個匿名函數,通過這個函數設置Person類中的一些屬性的值。以上的這種代碼風格就是DSL風格。 3. 通過apply()函數對DSL代碼進行優化 上述DSL風格的代碼中,擴展函數address()和person()還可以進一步通過Kotlin中的apply()函數進行優化,優化后的代碼如下所示: ``` fun Person.address(block: Address.() -> Unit) { address= Address().apply(block) } fun person(block: Person.() -> Unit): Person { return Person().apply(block) } ``` ### DSL的使用 在實際開發中,DSL會在特定的情況下使用,對代碼進行優化。在本節我們將通過傳統代碼與DSL代碼分別打印HTML標簽并進行對比來觀察DSL的優點。 #### 打印簡單的HTML標簽 在Kotlin中,通過普通API來打印HTML中最簡單的“<html></html>”標簽,具體代碼如下所示。 ``` //標簽對象 class Tag(var name: String) { var list = ArrayList<Tag>() //添加子標簽 fun addChild(tag: Tag) { list.add(tag) } //字符串 override fun toString(): String { val sb = StringBuilder() //開始標簽 sb.append("<$name>") //循環獲取子標簽并添加到sb中 list.forEach { sb.append(it) } //結束標簽 sb.append("</$name>") return sb.toString() } } fun main(args: Array<String>) { val html = Tag("html") println(html) } ``` 運行結果: ``` <html></html> ``` 由于上述代碼中的toString()方法中多次使用變量sb容易產生代碼冗余,因此為了不多次使用這個變量,也可以使用DSL中的apply()方法將上述代碼進行簡寫,具體代碼如下所示。 ``` //標簽對象 class DSLTag(var name: String) { var list = ArrayList<Tag>() //添加子標簽 fun addChild(tag: Tag) { list.add(tag) } //字符串 override fun toString(): String { return StringBuffer().apply { append("<$name>") list.forEach { append(it.toString()) } append("</$name>") }.toString() } } fun main(args: Array<String>) { val html = DSLTag("html") println(html) } ``` 運行結果: ``` <html></html> ``` #### 打印復雜的HTML標簽 在Kotlin中,可以通過普通API打印“`<html><head><title>你好</title></head><body></body><div></div></html>`”這些復雜的HTML標簽,具體代碼如下所示。 ``` class Html : HtmlTag("html") //Html標簽 class Head : HtmlTag("head") //Head標簽 class Body : HtmlTag("body") //Body標簽 //標簽對象 open class HtmlTag(var name: String) { var list = ArrayList<HtmlTag>() //添加子標簽 fun addChild(tag: HtmlTag) { list.add(tag) } //字符串 override fun toString(): String { return StringBuffer().apply { append("<$name>") list.forEach { append(it.toString()) } append("</$name>") }.toString() } } //Title標簽 class Title(var title: String) : HtmlTag("title") { override fun toString(): String { return "<title>$title</title>" } } fun main(args: Array<String>) { val html = Html() val head = Head() val title = Title("你好") val body = Body() head.addChild(title) //title添加到head中,順序不能變,關系不是很強,容易出錯 html.addChild(head) //head添加到html中 html.addChild(body) //body添加到html中 println(html) } ``` 運行結果: ``` <html><head><title>你好</title></head><body></body></html> ``` 上述代碼中,第33~35行代碼主要是通過addChild()方法將對應的標簽添加到<head>標簽或者<html>標簽中,這3行代碼的順序不能任意變動,否則容易出現內存泄露。如果使用第33~35行的代碼來設置對應的標簽,則標簽的子關系不是很強并且容易出現錯誤。因此,接下來我們通過DSL風格來修改上述代碼。修改后的代碼如下所示。 ``` class HtmlDSL : HtmlTagDSL("html") class HeadDSL : HtmlTagDSL("head") class BodyDSL : HtmlTagDSL("body") open class HtmlTagDSL(var name: String) { val list = ArrayList<HtmlTagDSL>() fun addChild(tag: HtmlTagDSL) { list.add(tag) } override fun toString(): String { return StringBuffer().apply { append("<$name>") list.forEach { append(it.toString()) } append("</$name>") }.toString() } } class TitleDSL : HtmlTagDSL("title") { var title: String? = null override fun toString(): String { return "<title>$title</title>" } } fun html(block: HtmlDSL.() -> Unit): HtmlTagDSL { return HtmlDSL().apply(block) } fun HtmlDSL.head(block: HeadDSL.() -> Unit) { addChild(HeadDSL().apply(block)) } fun HeadDSL.title(block: TitleDSL.() -> Unit) { addChild(TitleDSL().apply(block)) } fun HtmlDSL.body(block: BodyDSL.() -> Unit) { addChild(BodyDSL().apply(block)) } fun main(args: Array<String>) { val result = html { //擴展函數 head { title { title = "你好" } } body { } } println(result) } ``` 運行結果: ``` <html><head><title>你好</title></head><body></body></html> ``` ### Anko插件 Anko是一個DSL,它是用Kotlin寫的Android插件,由JetBrains公司開發的。Anko主要的作用是替代以前用XML的方式來生成UI布局。大家都知道,Android界面是通過XML來進行布局的,一個應用中通常有多個布局,當程序運行時,XML被轉化為Java代碼,這就導致程序很耗費資源。由于Anko是直接通過Java代碼來編寫布局文件的,不用進行轉化,因此使用Anko編寫Android界面的布局會更加簡單、快捷。接下來我們通過一個案例來演示Anko生成Android界面的布局。 首先看一下最熟悉的XML方式生成的UI布局,布局名稱以main.xml為例。具體代碼如下所示。 ``` <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="match_parent" android:layout_width="match_parent"> <EditText android:id="@+id/title" android:layout_width="match_parent" android:layout_heigh="wrap_content" android:hint="@string/title_hint" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/add" /> </LinearLayout> ``` 在上述XML文件中,主要是放置了1個EditText控件用于顯示一個標題,1個Button控件用于顯示一個添加的按鈕。 接下來使用Anko生成Android布局,與傳統的XML方式進行對比,并在這個布局中綁定Button按鈕的點擊事件。這段代碼是在Android項目中的Activity中添加的,在這里將這個Activity命名為MainActivity,具體代碼如下所示。 **MainActivity.java ** ``` verticalLayout { var title = editText { id = R.id.title hintResource = R.string.title_hint } button { textResource = R.string.add onClick { view -> { title.text = "Foo" } } } } ``` 從上述代碼可知,DSL的一個主要優點是只需很少的時間就可以理解和傳達某個領域的詳細信息。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看