DSL簡介
- DSL (領域特定語言)指的是專注於特定問題領域的計算機語言,即對一個特定問題的方案模型更高層次的抽象表達,使之更加簡單易懂。
- DSL只是問題解決方案模型的外部封裝,這個模型可能是一個API庫,也可能是一個完整的框架等
- 在Android中比較典型的例子使用DSL框架Anko來替代傳統的xml(類似Flutter創建佈局的方式)如下代碼塊:
UI {
// AnkoContext
verticalLayout {
padding = dip(30)
var title = editText {
// editText 視圖
id = R.id.todo_title
hintResource = R.string.title_hint
}
var content = editText {
id = R.id.todo_content
height = 400
hintResource = R.string.content_hint
}
button {
// button 視圖
id = R.id.todo_add
textResource = R.string.add_todo
textColor = Color.WHITE
setBackgroundColor(Color.DKGRAY)
onClick { _ -> createTodoFrom(title, content) }
}
}
}
DSL種類
內部DSL
- 內部DSL是指與項目中是使用的通用目的編程語言緊密相關的DSL
外部DSL
- 外部DSL是從零開始構建的語言,需要實現語法分析器,外部DSL與通用編程語言(GPL)類似,但是外部DSL更加專注於特定領域,創建 外部DSL和創建一種通用的百年城語言的過程是類似的,可以是編譯型和解釋型
Kotlin中的DSL支持
實現原理
- 擴展函數、擴展屬性
- 帶接收者的Lambda表達式(高階函數)
- invoke函數調用約定
實現集合類的流式Kotlin DSL
(可以模仿filter來實現)
// filter函數的入參是一個函數predicate:(T)->Boolean public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> { return filterTo(ArrayList<T>(), predicate) }
fun <T : Comparable<T>> List<T>.sort() {
Collections.sort(this) // this代表調用函數的對象
}
@Test
fun testSort() {
val list = listOf(1,7,10,5,6)
list.sort()
print(list) // [1, 5, 6, 7, 10]
}
實現SQL風格的集合類DSL
data class Student(var name: String, var sex: String, var score: Int)
fun <E> List<E>.select(): List<E> = this
fun <E> List<E>.where(prediction: (E) -> Boolean): List<E> {
val list = this
val result = arrayListOf<E>()
for (e in list) {
if (prediction(e)) {
result.add(e)
}
}
return result
}
fun <E> List<E>.and(prediction: (E) -> Boolean) : List<E> {
return where(prediction)
}
@Test
fun testSql() {
val students = listOf(
Student("w", "M", 90),
Student("e", "M", 80),
Student("r", "M", 60),
Student("t", "M", 80),
Student("y", "F", 100)
)
val queryResult = students.select()
.where { it.score >= 80 }
.and {it.sex == "M"}
print(queryResult)
// [Student(name=w, sex=M, score=90), Student(name=e, sex=M, score=80), Student(name=t, sex=M, score=80)]
}