使用kotlin寫自己的dsl
相比於java,kotlin對FP更加友好,支援擴充套件函式和操作符過載,這就為定義dsl提供了支援。
什麼是dsl呢?就是一種面向特定問題的語言。gradle就是是一種用groovy定義的dsl。而kotlin一樣對定義dsl提供了友好的支援。
本篇文章就來定義一個簡單用於配置hibernate框架的dsl,寫起來就像:
var conf= buildConfiguration{
connection {
username = "***"
password = "******"
url = "jdbc:mysql://localhost:3306/******"
driver = Driver::class.java
}
c3p0 {
max_size = 30
min_size = 10
timeout=5000
max_statements=100
idle_test_period=300
acquire_increment=2
validate=true
}
entity {
mapping = Client::class.java
mapping = Financial_account::class.java
mapping = FundHolding::class.java
mapping=Fund::class.java
}
dialect="org.hibernate.dialect.MySQL5InnoDBDialect"
}
上面是一個對hibernate的簡單配置,最後獲取一個configuration例項。通過使用dsl,可以避免在執行時解析xml檔案,同時又比使用java程式碼配置簡潔,兼具xml的結構化和java的高效。
那麼,這樣一個dsl是如何實現的呢?
先介紹一下預備知識:
擴充套件函式:
fun Type.foo():Unit{
...
}
這樣就為Type
物件建立了一個擴充套件函式,假如t
是Type
的一個例項,就可以:t.foo()
Type
稱作reciver
,而在foo
函式體內,可以使用this
訪問其public
成員,甚至可以省略this
,彷彿foo
函式是定義在class Type
內
而宣告一個函式的引數是擴充套件函式,一般的語法:fun funName(block:Type.(params...)->ReturnType):ReturnType{...}
關於擴充套件函式更多的用法,可以參考官方的文件
首先先宣告一個方法:
fun buildConfiguration(block:ConfigurationBuilder.()->Unit):Configuration{
var cfg=ConfigurationBuilder()
cfg.block()
return cfg.cfg
}
這個方法接收一個有receiver的lambda表示式,因為這樣在block的內部就可以直接訪問receiver的公共成員了,這一點很重要
緊接著對這個Configuration這個類進行定義
class ConfigurationBuilder{
val TAG="hibernate"
val cfg=Configuration()
var dialect:String? get() = null
set(value){
cfg.setProperty("$TAG.dialect",value!!)
}
inline fun connection(block:ConnectionBuilder.()->Unit)=ConnectionBuilder(cfg).block()
inline fun c3p0(block:C3p0Builder.()->Unit)=C3p0Builder(cfg).block()
inline fun entity(block:Entity.()->Unit)=Entity(cfg).block()
}
在裡面我定義了三個成員函式,分別對應前面示例中的
var conf= buildConfiguration{
connection {
...
}
c3p0 {
...
}
entity {
...
}
...
}
在這個lambda裡面,我就直接呼叫了buildConfiguration的成員函式,那麼物件引用呢?還記得我前面說過的嗎?buildConfiguration這個方法的引數是一個有receiver的lambda,而在buildConfiguration中宣告瞭一個ConfigurationBuilder物件並通過這個物件呼叫了這個lambda。那麼這個lambda就會在這個物件的上下文中,我們可以直接訪問它的公共成員,甚至可以使用this引用這個物件。
後續的步驟都差不多,我這裡為了省事直接就宣告瞭一個Configuration物件,並傳到了其他物件裡面
後面的原始碼
class ConnectionBuilder(val cfg:Configuration){//直接接受了一個configuration物件
val TAG="hibernate.connection"
var username:String? get() = null //重寫了setter和getter,防止屬性有field
set(name){
cfg.setProperty("$TAG.username",name!!) //直接硬編碼設定屬性
}
var password:String? get() = null
set(password) {
cfg.setProperty("$TAG.password", password!!)
}
var url:String? get() = null
set(url){
cfg.setProperty("$TAG.url",url!!)
}
var driver:Class<*>? get() = null
set(driver){
cfg.setProperty("$TAG.driver_class",driver!!.name)
}
var pool_size:Int? get() = null
set(size){
cfg.setProperty("$TAG.pool_size",size!!.toString())
}
}
//後面的都差不多。。。
class C3p0Builder(val cfg:Configuration){
val TAG="hibernate.c3p0"
var max_size:Int? get() = null
set(max_size){
cfg.setProperty("$TAG.max_size",max_size!!.toString())
}
var min_size:Int? get() = null
set(min_size){
cfg.setProperty("$TAG.min_size",min_size!!.toString())
}
var timeout:Int? get() = null
set(timeout){
cfg.setProperty("$TAG.timeout",timeout!!.toString())
}
var max_statements:Int? get() = null
set(max_stmt){
cfg.setProperty("$TAG.max_statements",max_stmt!!.toString())
}
var idle_test_period:Int? get() = null
set(idle_test_period){
cfg.setProperty("$TAG.idle_test_period",idle_test_period!!.toString())
}
var acquire_increment:Int? get() = null
set(acquire){
cfg.setProperty("$TAG.acquire_increment",acquire!!.toString())
}
var validate:Boolean? get() = null
set(validate){
cfg.setProperty("$TAG.validate",validate!!.toString())
}
}
class Entity(val cfg:Configuration){
var mapping:Class<*>?
get()=null
set(clazz){
cfg.addAnnotatedClass(clazz!!)
}
}
至此,一個簡單的dsl就完成了
總體來說,定義一個dsl的過程基本是一個遞迴下去的過程,每個步驟都很類似
相關文章
- 使用 Kotlin DSL 編寫網路爬蟲Kotlin爬蟲
- Kotlin 之旅6 使用Kotlin進行DSL開發Kotlin
- C# 使用Fluent API 建立自己的DSLC#API
- 使用Go和HCL構建您自己的DSLGo
- Kotlin 中使用 DSL 實現建造者模式Kotlin模式
- Gradle Kotlin DSL 1.0GradleKotlin
- Gradle Kotlin DSL遷移指南GradleKotlin
- line/kotlin-jdsl:用於JPA Criteria API的Kotlin DSLKotlinAPI
- Gradle Kotlin DSL , 你知道它嗎?GradleKotlin
- 【思貨】kotlin協程優雅的與Retrofit纏綿-kotlin DSL簡述Kotlin
- Kotlin實戰:使用DSL構建結構化API去掉冗餘的介面方法KotlinAPI
- 用 kotlin 來實現 dsl 風格的程式設計Kotlin程式設計
- es的複雜查詢測試,使用jest的dsl工具寫查詢語句
- Kotlin DSL C++專案引入OpenCV異常處理(轉)KotlinC++OpenCV
- Kotlin進階:動畫程式碼太醜,用DSL動畫庫拯救,像說話一樣寫程式碼喲!Kotlin動畫
- Kotlin學習手記——集合變換、序列、聚合、SAM轉換、DSLKotlin
- 為爬蟲框架構建Selenium模組、DSL模組(Kotlin實現)爬蟲框架架構Kotlin
- 自己寫的面試題,自己想的答案面試題
- 寫給自己看的在 Vue 下使用 Typescript 指北VueTypeScript
- 自己寫的加密方式加密
- 基於Axon框架使用Kotlin編寫的ES銀行案例框架Kotlin
- Kotlin + MVP + Flutter ,讓你可以在自己的專案中整合 Flutter 並使用KotlinMVPFlutter
- 如何使用Typora寫出自己的第一個部落格
- kotlin的Gson的使用Kotlin
- 編寫自己的 TypeScript CLITypeScript
- 自己寫的fabric指令碼指令碼
- 自己寫的ALV程式
- Query DSL
- 寫給自己看的體重記錄板使用指南
- 自己寫的和收藏的一寫php函式PHP函式
- kotlin的迴圈使用Kotlin
- 如果你想寫自己的Benchmark框架框架
- 寫給自己看的Typescript起步TypeScript
- 手寫一個自己的PromisePromise
- 手寫自己的MyBatis框架-SqlSessionMyBatis框架SQLSession
- 來學著寫自己的“jQuery”jQuery
- 編寫自己的Webpack LoaderWeb
- 放棄Javascript-使用kotlin編寫react前端應用之todoListJavaScriptKotlinReact前端