as## 1. 什么是DSL
DSL(领域特定语言)指的是专注于特定问题领域的计算机语言。不同于通用的计算机语言(GPL),领域特定语言只用在某些特定的领域。

DSL语言能让我们以一种更优雅、更简洁的方式的方式来表达和解决领域问题。
简单来讲就是对一个特定的问题的方案模型更高层次的抽象表达,使其更加简单易懂。

DSL只是问题问题解决方案模型的外部包装,这个模型可能是一个 API库,也可能是一个完整的框架等。DSL提供了思考特定领域问题的模型语言。这使得我们可以更加简单、高效的解决问题。

一个典型的例子就是用于替代Android开发中布局XML文件的DSL框架 Anko,它使用基于Kotlin的DSL来声明Android UI组件,而不是传统的XML

1.1 内部DSL
内部DSL是指与项目中使用的通用目的编程语言紧密相关的一类DSL。它基于通用编程语言实现
框架或程序库的API是否满足内部DSL的关键特征之一就是 它是否有一个流畅的接口。这样就能够用短小的对象表达式去组织一个原本很长的表达式,使它读起来更加自然

1.2 外部DSL
和内部DSL相反,外部DSL是从零开始构建的语言,需要实现语法分析器。
通常情况下,我们只需要实现内嵌式DSL,因为它更容易构建,并且具有很多与外部DSL相同的优势

2. Kotlin的DSL特性支持

在Kotlin中创建DSL,一般主要使用下面3个特性:

  • 扩展函数、扩展属性
  • 带接受者的Lambda表达式
  • invoke 函数调用约定

其实就是要 重写​​invoke()​

3. 实现集合类的流式Kotlin DSL

下面来创建一个给定文件名返回文件中每行文本字符串的流式API,代码风格是下面这样的:

fun main(args: Array<String>) {
val lines = "src/main/resources/data.txt"
.stream()
.buffered()
.reader("utf-8")
.readLines()
lines.forEach(::println)
}

//接下来来扩展函数:
fun String.stream() = FileInputStream(this)

fun FileInputStream.buffered() = BufferedInputStream(this)

fun InputStream.reader(charset: String) = InputStreamReader(this, charset)

fun Reader.readLines(): List<String> {
val result = arrayListOf<String>()
forEachLine {
result.add(it)
}
return result
}

4. 实现一个SQL风格的集合类DSL

我们来实现一个简单的在 Kotlin做 SQL操作的DSL

//创建一个学生类
data class Student(var name: String, var sex: String, var score: Int)

val students = listOf(
Student("Rikka","M",90),
Student("xiaohu","F",76),
Student("uzi","M",105),
Student("karsa","F",50)
)

接下来定义sql的查询方法:

fun <E> List<E>.select(): List<E> = this

//参数是一个 入参为E,出参为Boolean的函数
fun <E> List<E>.where(predicate: (E) -> Boolean): List<E> {
val list = this
val result = arrayListOf<E>()
for (e in list) {
if (predicate(e)) {
result.add(e)
}
}
return result
}

fun <E> List<E>.and(predicate: (E) -> Boolean): List<E> {
return where(predicate)
}