Kotlin自定义DSL真实案例
下面我将通过几个完整的真实案例来展示Kotlin自定义DSL的实践应用。
案例1:HTTP API客户端DSL
目标:创建声明式的HTTP请求构建器
// ================ HTTP DSL 核心 ================
/**
* HTTP方法枚举
*/
enum class HttpMethod {
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
}
/**
* HTTP客户端配置
*/
data class HttpClientConfig(
var baseUrl: String = "",
var connectTimeout: Long = 10_000,
var readTimeout: Long = 10_000,
var writeTimeout: Long = 10_000,
var headers: MutableMap<String, String> = mutableMapOf(
"Content-Type" to "application/json"
)
)
/**
* HTTP请求构建器
*/
class HttpRequestBuilder internal constructor() {
var method: HttpMethod = HttpMethod.GET
var path: String = ""
val headers: MutableMap<String, String> = mutableMapOf()
val queryParams: MutableMap<String, Any> = mutableMapOf()
var body: Any? = null
fun header(name: String, value: String) {
headers[name] = value
}
fun queryParam(name: String, value: Any) {
queryParams[name] = value
}
fun jsonBody(init: JsonBodyBuilder.() -> Unit) {
body = JsonBodyBuilder().apply(init).build()
}
fun formBody(init: FormBodyBuilder.() -> Unit) {
body = FormBodyBuilder().apply(init).build()
}
internal fun build(): HttpRequest {
return HttpRequest(method, path, headers, queryParams, body)
}
}
/**
* JSON请求体构建器
*/
class JsonBodyBuilder {
private val properties = mutableMapOf<String, Any?>()
operator fun String.invoke(value: Any?) {
properties[this] = value
}
fun property(name: String, value: Any?) {
properties[name] = value
}
fun array(name: String, init: JsonArrayBuilder.() -> Unit) {
properties[name] = JsonArrayBuilder().apply(init).build()
}
fun obj(name: String, init: JsonBodyBuilder.() -> Unit) {
properties[name] = JsonBodyBuilder().apply(init).build()
}
internal fun build(): Map<String, Any?> = properties
}
/**
* JSON数组构建器
*/
class JsonArrayBuilder {
private val items = mutableListOf<Any?>()
fun add(value: Any?) {
items.add(value)
}
fun addObj(init: JsonBodyBuilder.() -> Unit) {
items.add(JsonBodyBuilder().apply(init).build())
}
fun addArray(init: JsonArrayBuilder.() -> Unit) {
items.add(JsonArrayBuilder().apply(init).build())
}
internal fun build(): List<Any?> = items
}
/**
* 表单请求体构建器
*/
class FormBodyBuilder {
private val params = mutableMapOf<String, Any>()
operator fun String.invoke(value: Any) {
params[this] = value
}
fun param(name: String, value: Any) {
params[name] = value
}
internal fun build(): Map<String, Any> = params
}
/**
* HTTP请求数据类
*/
data class HttpRequest(
val method: HttpMethod,
val path: String,
val headers: Map<String, String>,
val queryParams: Map<String, Any>,
val body: Any?
)
/**
* HTTP响应数据类
*/
data class HttpResponse<T>(
val statusCode: Int,
val body: T?,
val headers: Map<String, String>
)
/**
* HTTP客户端
*/
class HttpClient(private val config: HttpClientConfig) {
suspend inline fun <reified T> request(
init: HttpRequestBuilder.() -> Unit
): HttpResponse<T> {
val requestBuilder = HttpRequestBuilder().apply(init)
// 合并全局配置和请求特定的headers
val allHeaders = config.headers.toMutableMap()
allHeaders.putAll(requestBuilder.headers)
// 构建完整URL
val url = buildUrl(requestBuilder)
// 在实际项目中,这里会使用Ktor、OkHttp等HTTP客户端
println("发送HTTP请求:")
println(" URL: $url")
println(" 方法: ${requestBuilder.method}")
println(" Headers: $allHeaders")
println(" Query参数: ${requestBuilder.queryParams}")
println(" Body: ${requestBuilder.body}")
// 模拟响应
return HttpResponse(
statusCode = 200,
body = null,
headers = mapOf("Content-Type" to "application/json")
)
}
private fun buildUrl(requestBuilder: HttpRequestBuilder): String {
val base = if (config.baseUrl.endsWith("/"))
config.baseUrl.dropLast(1) else config.baseUrl
val path = if (requestBuilder.path.startsWith("/"))
requestBuilder.path else "/${requestBuilder.path}"
val queryString = if (requestBuilder.queryParams.isNotEmpty()) {
"?" + requestBuilder.queryParams.entries
.joinToString("&") { "${it.key}=${it.value}" }
} else ""
return "$base$path$queryString"
}
}
/**
* HTTP DSL入口函数
*/
fun httpClient(init: HttpClientConfig.() -> Unit): HttpClient {
val config = HttpClientConfig().apply(init)
return HttpClient(config)
}
// ================ 使用示例 ================
// 示例1:简单的GET请求
fun example1() {
val client = httpClient {
baseUrl = "https://api.example.com"
headers["User-Agent"] = "MyApp/1.0"
}
// 在协程中执行
/*
runBlocking {
val response = client.request<String> {
method = HttpMethod.GET
path = "/users"
queryParam("page", 1)
queryParam("limit", 20)
header("Authorization", "Bearer token123")
}
}
*/
}
// 示例2:复杂的POST请求
fun example2() {
val client = httpClient {
baseUrl = "https://api.example.com"
connectTimeout = 15000
readTimeout = 30000
}
// 创建嵌套JSON请求
/*
runBlocking {
val response = client.request<Map<String, Any>> {
method = HttpMethod.POST
path = "/orders"
jsonBody {
"orderId"("12345")
"customer" {
"name"("张三")
"email"("zhangsan@example.com")
"phone"("13800138000")
}
"items" array {
addObj {
"productId"("P001")
"quantity"(2)
"price"(99.99)
}
addObj {
"productId"("P002")
"quantity"(1)
"price"(199.99)
}
}
"shippingAddress" {
"city"("北京")
"district"("朝阳区")
"street"("建国门外大街")
}
}
}
}
*/
}
// 示例3:表单提交
fun example3() {
val client = httpClient {
baseUrl = "https://auth.example.com"
}
/*
runBlocking {
val response = client.request<String> {
method = HttpMethod.POST
path = "/login"
formBody {
"username"("zhangsan")
"password"("secret123")
"grant_type"("password")
}
header("Content-Type", "application/x-www-form-urlencoded")
}
}
*/
}
案例2:数据库查询DSL
目标:创建类型安全的SQL查询构建器
// ================ SQL DSL 核心 ================
/**
* SQL操作符
*/
sealed class SqlOperator(val symbol: String) {
object Equals : SqlOperator("=")
object NotEquals : SqlOperator("!=")
object GreaterThan : SqlOperator(">")
object GreaterThanOrEquals : SqlOperator(">=")
object LessThan : SqlOperator("<")
object LessThanOrEquals : SqlOperator("<=")
object Like : SqlOperator("LIKE")
object In : SqlOperator("IN")
object NotIn : SqlOperator("NOT IN")
object IsNull : SqlOperator("IS NULL")
object IsNotNull : SqlOperator("IS NOT NULL")
}
/**
* SQL连接条件
*/
sealed class SqlJoinType {
object Inner : SqlJoinType()
object Left : SqlJoinType()
object Right : SqlJoinType()
object Full : SqlJoinType()
}
/**
* 查询条件构建器
*/
class WhereClauseBuilder {
private val conditions = mutableListOf<String>()
private val parameters = mutableMapOf<String, Any>()
fun and(condition: String, paramName: String? = null, value: Any? = null) {
conditions.add("AND $condition")
paramName?.let { parameters[it] = value!! }
}
fun or(condition: String, paramName: String? = null, value: Any? = null) {
conditions.add("OR $condition")
paramName?.let { parameters[it] = value!! }
}
fun eq(column: String, value: Any) {
val paramName = "p${parameters.size}"
conditions.add("AND $column = :$paramName")
parameters[paramName] = value
}
fun ne(column: String, value: Any) {
val paramName = "p${parameters.size}"
conditions.add("AND $column != :$paramName")
parameters[paramName] = value
}
fun gt(column: String, value: Any) {
val paramName = "p${parameters.size}"
conditions.add("AND $column > :$paramName")
parameters[paramName] = value
}
fun `in`(column: String, values: List<Any>) {
val paramNames = values.mapIndexed { index, _ ->
"p${parameters.size + index}"
}
conditions.add("AND $column IN (${paramNames.joinToString { ":$it" }})")
paramNames.forEachIndexed { index, name ->
parameters[name] = values[index]
}
}
fun like(column: String, pattern: String) {
val paramName = "p${parameters.size}"
conditions.add("AND $column LIKE :$paramName")
parameters[paramName] = pattern
}
fun isNull(column: String) {
conditions.add("AND $column IS NULL")
}
fun isNotNull(column: String) {
conditions.add("AND $column IS NOT NULL")
}
internal fun build(): Pair<String, Map<String, Any>> {
val whereClause = if (conditions.isEmpty()) {
""
} else {
"WHERE " + conditions.joinToString(" ")
.removePrefix("AND ")
.removePrefix("OR ")
}
return whereClause to parameters
}
}
/**
* 排序方向
*/
enum class SortDirection {
ASC, DESC
}
/**
* 查询构建器
*/
class SelectQueryBuilder<T : Any> {
private var distinct: Boolean = false
private val columns = mutableListOf<String>()
private val tables = mutableListOf<String>()
private val joins = mutableListOf<String>()
private val whereBuilder = WhereClauseBuilder()
private val groupByColumns = mutableListOf<String>()
private val havingBuilder = WhereClauseBuilder()
private val orderByColumns = mutableListOf<String>()
private var limitCount: Int? = null
private var offsetCount: Int? = null
fun distinct() {
distinct = true
}
fun select(vararg cols: String) {
columns.addAll(cols)
}
fun from(table: String) {
tables.add(table)
}
fun join(
type: SqlJoinType = SqlJoinType.Inner,
table: String,
on: String
) {
val joinType = when (type) {
SqlJoinType.Inner -> "INNER JOIN"
SqlJoinType.Left -> "LEFT JOIN"
SqlJoinType.Right -> "RIGHT JOIN"
SqlJoinType.Full -> "FULL JOIN"
}
joins.add("$joinType $table ON $on")
}
fun where(init: WhereClauseBuilder.() -> Unit) {
whereBuilder.init()
}
fun groupBy(vararg columns: String) {
groupByColumns.addAll(columns)
}
fun having(init: WhereClauseBuilder.() -> Unit) {
havingBuilder.init()
}
fun orderBy(column: String, direction: SortDirection = SortDirection.ASC) {
orderByColumns.add("$column $direction")
}
fun limit(count: Int) {
limitCount = count
}
fun offset(count: Int) {
offsetCount = count
}
internal fun build(): Pair<String, Map<String, Any>> {
val selectClause = if (distinct) "SELECT DISTINCT" else "SELECT"
val columnsClause = if (columns.isEmpty()) "*" else columns.joinToString(", ")
val fromClause = "FROM ${tables.joinToString(", ")}"
val joinClause = if (joins.isNotEmpty()) joins.joinToString(" ") else ""
val (whereClause, whereParams) = whereBuilder.build()
val (havingClause, havingParams) = havingBuilder.build()
val groupByClause = if (groupByColumns.isNotEmpty()) {
"GROUP BY ${groupByColumns.joinToString(", ")}"
} else ""
val orderByClause = if (orderByColumns.isNotEmpty()) {
"ORDER BY ${orderByColumns.joinToString(", ")}"
} else ""
val limitClause = limitCount?.let { "LIMIT $it" } ?: ""
val offsetClause = offsetCount?.let { "OFFSET $it" } ?: ""
val sql = listOf(
selectClause, columnsClause,
fromClause, joinClause,
whereClause, groupByClause,
havingClause, orderByClause,
limitClause, offsetClause
).filter { it.isNotBlank() }.joinToString(" ")
val allParams = mutableMapOf<String, Any>()
allParams.putAll(whereParams)
allParams.putAll(havingParams)
return sql to allParams
}
}
/**
* 插入构建器
*/
class InsertQueryBuilder {
private var table: String = ""
private val columns = mutableListOf<String>()
private val values = mutableMapOf<String, Any>()
fun into(table: String) {
this.table = table
}
operator fun String.invoke(value: Any) {
columns.add(this)
values[this] = value
}
fun column(name: String, value: Any) {
columns.add(name)
values[name] = value
}
internal fun build(): Pair<String, Map<String, Any>> {
val columnsClause = columns.joinToString(", ", "(", ")")
val paramNames = columns.mapIndexed { index, _ -> "p$index" }
val valuesClause = paramNames.joinToString(", ", "VALUES (", ")")
val sql = "INSERT INTO $table $columnsClause $valuesClause"
val params = paramNames.mapIndexed { index, paramName ->
paramName to values[columns[index]]!!
}.toMap()
return sql to params
}
}
/**
* 更新构建器
*/
class UpdateQueryBuilder {
private var table: String = ""
private val sets = mutableMapOf<String, Any>()
private val whereBuilder = WhereClauseBuilder()
fun table(table: String) {
this.table = table
}
fun set(column: String, value: Any) {
sets[column] = value
}
fun where(init: WhereClauseBuilder.() -> Unit) {
whereBuilder.init()
}
internal fun build(): Pair<String, Map<String, Any>> {
val setClause = sets.entries.mapIndexed { index, (column, _) ->
"$column = :set$index"
}.joinToString(", ")
val (whereClause, whereParams) = whereBuilder.build()
val sql = "UPDATE $table SET $setClause $whereClause"
val params = mutableMapOf<String, Any>()
sets.entries.forEachIndexed { index, (_, value) ->
params["set$index"] = value
}
params.putAll(whereParams)
return sql to params
}
}
/**
* 数据库DSL入口
*/
object DatabaseDSL {
fun <T : Any> select(init: SelectQueryBuilder<T>.() -> Unit): Pair<String, Map<String, Any>> {
return SelectQueryBuilder<T>().apply(init).build()
}
fun insert(init: InsertQueryBuilder.() -> Unit): Pair<String, Map<String, Any>> {
return InsertQueryBuilder().apply(init).build()
}
fun update(init: UpdateQueryBuilder.() -> Unit): Pair<String, Map<String, Any>> {
return UpdateQueryBuilder().apply(init).build()
}
fun deleteFrom(table: String): String {
return "DELETE FROM $table"
}
}
// ================ 使用示例 ================
// 示例1:复杂查询
fun buildComplexQuery() {
val (sql, params) = DatabaseDSL.select<User> {
distinct()
select("id", "name", "email", "created_at")
from("users")
join(SqlJoinType.Left, "orders", "users.id = orders.user_id")
where {
eq("status", "ACTIVE")
gt("age", 18)
`in`("city", listOf("北京", "上海", "广州"))
like("name", "%张%")
}
groupBy("users.id")
having {
gt("COUNT(orders.id)", 5)
}
orderBy("created_at", SortDirection.DESC)
limit(20)
offset(0)
}
println("SQL: $sql")
println("参数: $params")
}
// 示例2:插入数据
fun buildInsertQuery() {
val (sql, params) = DatabaseDSL.insert {
into("users")
"name"("张三")
"email"("zhangsan@example.com")
"age"(25)
"city"("北京")
"created_at"(System.currentTimeMillis())
}
println("SQL: $sql")
println("参数: $params")
}
// 示例3:更新数据
fun buildUpdateQuery() {
val (sql, params) = DatabaseDSL.update {
table("users")
set("email", "newemail@example.com")
set("age", 26)
where {
eq("id", 123)
}
}
println("SQL: $sql")
println("参数: $params")
}
// 实体类示例
data class User(
val id: Int,
val name: String,
val email: String,
val age: Int,
val city: String,
val createdAt: Long
)
案例3:配置管理DSL
目标:创建类型安全的应用程序配置构建器
// ================ 配置管理DSL ================
/**
* 配置值密封类
*/
sealed class ConfigValue {
data class StringValue(val value: String) : ConfigValue()
data class IntValue(val value: Int) : ConfigValue()
data class BooleanValue(val value: Boolean) : ConfigValue()
data class DoubleValue(val value: Double) : ConfigValue()
data class ListValue(val values: List<ConfigValue>) : ConfigValue()
data class MapValue(val map: Map<String, ConfigValue>) : ConfigValue()
fun asString(): String = when (this) {
is StringValue -> value
else -> throw IllegalStateException("不是字符串类型")
}
fun asInt(): Int = when (this) {
is IntValue -> value
else -> throw IllegalStateException("不是整数类型")
}
fun asBoolean(): Boolean = when (this) {
is BooleanValue -> value
else -> throw IllegalStateException("不是布尔类型")
}
fun asDouble(): Double = when (this) {
is DoubleValue -> value
else -> throw IllegalStateException("不是浮点数类型")
}
fun asList(): List<ConfigValue> = when (this) {
is ListValue -> values
else -> throw IllegalStateException("不是列表类型")
}
fun asMap(): Map<String, ConfigValue> = when (this) {
is MapValue -> map
else -> throw IllegalStateException("不是映射类型")
}
}
/**
* 配置构建器
*/
class ConfigBuilder {
private val config = mutableMapOf<String, ConfigValue>()
operator fun String.invoke(value: String) {
config[this] = ConfigValue.StringValue(value)
}
operator fun String.invoke(value: Int) {
config[this] = ConfigValue.IntValue(value)
}
operator fun String.invoke(value: Boolean) {
config[this] = ConfigValue.BooleanValue(value)
}
operator fun String.invoke(value: Double) {
config[this] = ConfigValue.DoubleValue(value)
}
operator fun String.invoke(vararg values: ConfigValue) {
config[this] = ConfigValue.ListValue(values.toList())
}
fun list(name: String, init: ListBuilder.() -> Unit) {
config[name] = ConfigValue.ListValue(ListBuilder().apply(init).build())
}
fun map(name: String, init: ConfigBuilder.() -> Unit) {
config[name] = ConfigValue.MapValue(ConfigBuilder().apply(init).build())
}
fun env(name: String, defaultValue: String? = null) {
val envValue = System.getenv(name) ?: defaultValue
envValue?.let {
config[name] = ConfigValue.StringValue(it)
}
}
fun prop(name: String, defaultValue: String? = null) {
val propValue = System.getProperty(name) ?: defaultValue
propValue?.let {
config[name] = ConfigValue.StringValue(it)
}
}
internal fun build(): Map<String, ConfigValue> = config
}
/**
* 列表构建器
*/
class ListBuilder {
private val list = mutableListOf<ConfigValue>()
fun add(value: String) {
list.add(ConfigValue.StringValue(value))
}
fun add(value: Int) {
list.add(ConfigValue.IntValue(value))
}
fun add(value: Boolean) {
list.add(ConfigValue.BooleanValue(value))
}
fun add(value: Double) {
list.add(ConfigValue.DoubleValue(value))
}
fun addList(init: ListBuilder.() -> Unit) {
list.add(ConfigValue.ListValue(ListBuilder().apply(init).build()))
}
fun addMap(init: ConfigBuilder.() -> Unit) {
list.add(ConfigValue.MapValue(ConfigBuilder().apply(init).build()))
}
internal fun build(): List<ConfigValue> = list
}
/**
* 配置文件类
*/
class Config(private val config: Map<String, ConfigValue>) {
fun getString(key: String): String? {
return config[key]?.asString()
}
fun getInt(key: String): Int? {
return config[key]?.asInt()
}
fun getBoolean(key: String): Boolean? {
return config[key]?.asBoolean()
}
fun getDouble(key: String): Double? {
return config[key]?.asDouble()
}
fun getList(key: String): List<ConfigValue>? {
return config[key]?.asList()
}
fun getMap(key: String): Map<String, ConfigValue>? {
return config[key]?.asMap()
}
fun getSubConfig(key: String): Config? {
return getMap(key)?.let { Config(it) }
}
fun requireString(key: String): String {
return getString(key) ?: throw IllegalArgumentException("配置项 $key 不存在或不是字符串类型")
}
fun requireInt(key: String): Int {
return getInt(key) ?: throw IllegalArgumentException("配置项 $key 不存在或不是整数类型")
}
override fun toString(): String {
return formatMap(config, 0)
}
private fun formatMap(map: Map<String, ConfigValue>, indent: Int): String {
val indentStr = " ".repeat(indent)
return map.entries.joinToString("\n") { (key, value) ->
"$indentStr$key: ${formatValue(value, indent + 1)}"
}
}
private fun formatValue(value: ConfigValue, indent: Int): String = when (value) {
is ConfigValue.StringValue -> "\"${value.value}\""
is ConfigValue.IntValue -> value.value.toString()
is ConfigValue.BooleanValue -> value.value.toString()
is ConfigValue.DoubleValue -> value.value.toString()
is ConfigValue.ListValue -> formatList(value.values, indent)
is ConfigValue.MapValue -> "{\n${formatMap(value.map, indent)}\n${" ".repeat(indent - 1)}}"
}
private fun formatList(list: List<ConfigValue>, indent: Int): String {
val indentStr = " ".repeat(indent)
return list.joinToString(
prefix = "[\n",
separator = ",\n",
postfix = "\n${" ".repeat(indent - 1)}]"
) { value ->
"$indentStr${formatValue(value, indent + 1)}"
}
}
}
/**
* 配置DSL入口
*/
fun config(init: ConfigBuilder.() -> Unit): Config {
val configMap = ConfigBuilder().apply(init).build()
return Config(configMap)
}
// ================ 使用示例 ================
// 示例:构建完整的应用配置
fun buildApplicationConfig() {
val appConfig = config {
// 基础配置
"app.name"("订单管理系统")
"app.version"("1.0.0")
"app.debug"(false)
// 服务器配置
map("server") {
"host"("0.0.0.0")
"port"(8080)
"contextPath"("/api")
"maxThreads"(200)
"idleTimeout"(30000)
}
// 数据库配置
map("database") {
map("primary") {
"driver"("org.postgresql.Driver")
"url"("jdbc:postgresql://localhost:5432/order_db")
"username"("admin")
// 从环境变量读取密码
env("DB_PASSWORD")
"poolSize"(20)
"connectionTimeout"(5000)
}
map("replica") {
"driver"("org.postgresql.Driver")
"url"("jdbc:postgresql://replica:5432/order_db")
"username"("readonly")
env("DB_REPLICA_PASSWORD", "default_pass")
"poolSize"(10)
}
}
// Redis配置
map("redis") {
map("cluster") {
list("nodes") {
add("redis1:6379")
add("redis2:6379")
add("redis3:6379")
}
"password"("redis_pass_123")
"timeout"(2000)
"maxTotal"(100)
"maxIdle"(20)
}
}
// 缓存配置
map("cache") {
map("user") {
"enabled"(true)
"ttl"(3600000) // 1小时
"maxSize"(10000)
}
map("product") {
"enabled"(true)
"ttl"(1800000) // 30分钟
"maxSize"(50000)
}
}
// 消息队列配置
map("mq") {
"type"("kafka")
map("kafka") {
"bootstrapServers"("kafka1:9092,kafka2:9092")
"acks"("all")
"retries"(3)
list("topics") {
add("order.created")
add("order.updated")
add("payment.processed")
}
}
}
// 外部服务配置
map("externalServices") {
map("paymentGateway") {
"baseUrl"("https://api.payment.com/v1")
"apiKey"("pk_test_123456")
"timeout"(10000)
"retryAttempts"(3)
}
map("smsService") {
"provider"("阿里云")
"accessKey"("your_access_key")
env("SMS_SECRET")
"signName"("订单系统")
}
}
// 业务配置
map("business") {
map("order") {
"autoCancelMinutes"(30)
"maxItemsPerOrder"(50)
"allowPartialShipment"(true)
}
map("inventory") {
"lowStockThreshold"(10)
"reservationTimeout"(300000) // 5分钟
}
map("delivery") {
list("supportedRegions") {
add("北京")
add("上海")
add("广州")
add("深圳")
}
"expressFee"(15.0)
"freeShippingThreshold"(199.0)
}
}
// 监控配置
map("monitoring") {
"enabled"(true)
"metricsPrefix"("order.system")
map("prometheus") {
"port"(9095)
"path"("/metrics")
}
map("logging") {
"level"("INFO")
"filePath"("/var/log/order-system.log")
"maxSize"(104857600) // 100MB
"maxBackups"(10)
}
}
}
// 使用配置
println("应用配置:")
println(appConfig)
// 访问具体配置
val serverHost = appConfig.getSubConfig("server")?.getString("host")
val dbUrl = appConfig.getSubConfig("database")?.getSubConfig("primary")?.getString("url")
val redisNodes = appConfig.getSubConfig("redis")?.getSubConfig("cluster")?.getList("nodes")
println("\n关键配置:")
println("服务器主机: $serverHost")
println("数据库URL: $dbUrl")
println("Redis节点: $redisNodes")
}
// ================ 配置验证器 ================
/**
* 配置验证器DSL
*/
class ConfigValidator(private val config: Config) {
fun validate(init: ValidationBuilder.() -> Unit): ValidationResult {
return ValidationBuilder(config).apply(init).validate()
}
}
class ValidationBuilder(private val config: Config) {
private val errors = mutableListOf<String>()
fun require(key: String) {
if (config.getString(key) == null) {
errors.add("缺少必需的配置项: $key")
}
}
fun requireInRange(key: String, min: Int, max: Int) {
val value = config.getInt(key)
when {
value == null -> errors.add("配置项 $key 不存在")
value < min || value > max -> errors.add("配置项 $key 的值 $value 不在范围 [$min, $max] 内")
}
}
fun requireNotEmpty(key: String) {
val value = config.getString(key)
if (value.isNullOrEmpty()) {
errors.add("配置项 $key 不能为空")
}
}
fun requireMatch(key: String, regex: Regex) {
val value = config.getString(key)
if (value != null && !regex.matches(value)) {
errors.add("配置项 $key 的值 '$value' 不符合格式要求")
}
}
internal fun validate(): ValidationResult {
return ValidationResult(errors)
}
}
data class ValidationResult(val errors: List<String>) {
val isValid: Boolean get() = errors.isEmpty()
fun throwIfInvalid() {
if (!isValid) {
throw IllegalArgumentException("配置验证失败:\n${errors.joinToString("\n")}")
}
}
}
// 使用验证器
fun validateConfig(config: Config) {
val result = ConfigValidator(config).validate {
require("app.name")
require("app.version")
requireInRange("server.port", 1024, 65535)
requireNotEmpty("database.primary.url")
requireMatch("database.primary.url", Regex("^jdbc:\\w+://.+$"))
}
if (result.isValid) {
println("配置验证通过")
} else {
println("配置验证失败:")
result.errors.forEach { println(" - $it") }
}
}
最佳实践总结
1. 类型安全
- 使用密封类定义类型
- 充分利用Kotlin的类型推断
- 为DSL元素提供明确的返回类型
2. 流畅的API设计
- 使用
infix函数简化语法 - 利用操作符重载
- 提供合理的默认值
3. 错误处理
- 在编译时捕获尽可能多的错误
- 提供清晰的错误消息
- 支持配置验证
4. 扩展性
- 设计可扩展的DSL结构
- 支持嵌套和组合
- 提供钩子函数进行自定义
5. 性能考虑
- 使用内联函数减少开销
- 避免不必要的对象创建
- 懒加载和缓存配置
6. 文档和示例
- 为DSL提供详细的文档
- 包含丰富的使用示例
- 提供迁移指南
这些真实案例展示了Kotlin DSL的强大能力,可以帮助你创建类型安全、表达力强且易于维护的领域特定语言。