日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

如何使用Kotlin開發DSL?

來源: 責編: 時間:2024-01-08 09:14:00 211觀看
導讀譯者 | 布加迪審校 | 重樓程序員總是在爭論哪種語言是最好的。我們曾比較過C和Pascal,但時過境遷。Python與Ruby之爭和Java與C#之爭早已遠去。每種語言有其優缺點。理想情況下,我們希望擴展語言以滿足自己的需要。程序

譯者 | 布加迪mA028資訊網——每日最新資訊28at.com

審校 | 重樓mA028資訊網——每日最新資訊28at.com

程序員總是在爭論哪種語言是最好的。我們曾比較過CPascal,但時過境遷。Python與Ruby之爭Java與C#之爭遠去。每種語言有其優缺點。理想情況下,我們希望擴展語言以滿足自己的需要。程序員早就有這樣的機會。我們知道元編程(即創建用來創建程序的程序)的不同方式。在C中,連不起眼的宏允許您小的描述生成大代碼。然而,這些宏是不可靠的、有限的,表達力不強?,F代語言擁有極富表現力的擴展方式其中一種語言是KotlinmA028資訊網——每日最新資訊28at.com

一、領域特定語言的定義

領域特定語言DSL一種專門為特定主題領域開發的語言,與Java、C#和C++等通用語言不同。這意味著它描述主題領域的任務更容易、更方便、更有表現力,但同時它解決日常任務也不方便、不實用,即它不是一種通用語言。作為DSL的一個例子,您可以使用正則表達式語言。正則表達式的主題領域是字符串格式。mA028資訊網——每日最新資訊28at.com

要檢查字符串是否符合格式,只需使用支持正則表達式的庫就夠了mA028資訊網——每日最新資訊28at.com

private boolean isIdentifierOrInteger(String s) { return s.matches("^//s*(//w+//d*|//d+)$"); }

如果您檢查字符串是否符合通用語言(比如Java中的指定格式,您將得到以下代碼mA028資訊網——每日最新資訊28at.com

private boolean isIdentifierOrInteger(String s) { int index = 0; while (index < s.length() && isSpaceChar(s.charAt(index))) { index++; } if (index == s.length()) { return false; } if (isLetter(s.charAt(index))) { index++; while (index < s.length() && isLetter(s.charAt(index))) index++; while (index < s.length() && isDigit(s.charAt(index))) index++; } else if (Character.isDigit(s.charAt(index))) { while (index < s.length() && isDigit(s.charAt(index))) index++; } return index == s.length(); }

上面的代碼比正則表達式更難閱讀,更容易出錯,更難以變更。mA028資訊網——每日最新資訊28at.com

DSL的其他常見例HTML、CSS、SQL、UMLBPMN后兩使用圖形符號。不僅開發人員使用DSL,測試人員和非IT專家也使用DSL。mA028資訊網——每日最新資訊28at.com

二、DSL的類型

DSL分為兩種類型外部和內部。外部DSL語言有自己的語法,它們不依賴用來實現持的通用編程語言。mA028資訊網——每日最新資訊28at.com

外部DSL的優缺點mA028資訊網——每日最新資訊28at.com

  • 使用不同語言/現成庫生成代碼
  • 設置語法方面擁有更多選項
  1. 使用專門的工具ANTLR、yacclex
  2. 有時很難描述語法
  3. 沒有IDE支持,您需要編寫插件

內部DSL基于特定的通用編程語言(宿主語言。也就是說,在宿主語言的標準工具的幫助下,創建允許您編寫更緊湊的庫。Fluent API方法就是一個例子。mA028資訊網——每日最新資訊28at.com

內部DSL優缺點:mA028資訊網——每日最新資訊28at.com

  • 使用宿主語言表達式為基礎
  • 很容易將DSL嵌入到宿主語言的代碼中,反之亦然
  • 不需要生成代碼
  • 可以作為宿主語言的子程序進行調試
  1. 設置語法方面機會有限

三、一個真實的例子

最近,我們公司需要創建DSL。我們的產品已經實現了購買驗收功能。該模塊是BPM業務流程管理的一個小型引擎。業務流程常以圖形方式表示。比如說,下面的BPMN標注顯示了一個由執行任務1,然后并行執行任務2和任務3組成的流程。mA028資訊網——每日最新資訊28at.com

mA028資訊網——每日最新資訊28at.com

能夠以編程方式創建業務流程對我們來說非常重要,包括動態構建路徑、為審批階段設置執行者、為階段執行設置截止日期等。為此,我們先嘗試使用Fluent API方法來解決這個問題。mA028資訊網——每日最新資訊28at.com

然后我們得出結論使用Fluent API設置驗收路徑仍然很麻煩,我們的團隊考慮了創建自己的DSL這種方案。我們研究了基于Kotlin外部DSL和內部DSL的驗收路徑是什么樣子(因為我們的產品代碼是用Java和Kotlin編寫的。mA028資訊網——每日最新資訊28at.com

外部DSLmA028資訊網——每日最新資訊28at.com

Acceptance addStep executor: HEAD_OF_DEPARTMENT duration: 7 days protocol should be formed parallel addStep executor: FINANCE_DEPARTMENT or CTO or CEO condition: ${!request.isInternal} duration: 7 work days after start date addStep executor: CTO dueDate: 2022-12-08 08:00 PST can change addStep executor: SECRETARY protocol should be signed內部DSL:acceptance { addStep {  executor = HEAD_OF_DEPARTMENT  duration = days(7)  protocol shouldBe formed } parallel {  addStep {   executor = FINANCE_DEPARTMENT or CTO or CEO   condition = !request.isInternal   duration = startDate() + workDays(7)  }  addStep {   executor = CTO dueDate = "2022-12-08 08:00" timezone PST   +canChange  } } addStep {  executor = SECRETARY  protocol shouldBe signed }}

除了花括號外,這兩個選項幾乎一樣。因此,決定不浪費時間和精力開發外部DSL,而是創建內部DSL。mA028資訊網——每日最新資訊28at.com

四、實施DSL的基本結構

不妨開始開發一個對象模型mA028資訊網——每日最新資訊28at.com

interface AcceptanceElementclass StepContext : AcceptanceElement { lateinit var executor: ExecutorCondition var duration: Duration? = null var dueDate: ZonedDateTime? = null val protocol = Protocol() var condition = true var canChange = ChangePermission()}class AcceptanceContext : AcceptanceElement { val elements = mutableListOf<AcceptanceElement>() fun addStep(init: StepContext.() -> Unit) {  elements += StepContext().apply(init) } fun parallel(init: AcceptanceContext.() -> Unit) {  elements += AcceptanceContext().apply(init) }}object acceptance { operator fun invoke(init: AcceptanceContext.() -> Unit): AcceptanceContext {  val acceptanceContext = AcceptanceContext()  acceptanceContext.init()  return acceptanceContext }}

Lambdas

首先看一下AcceptanceContext類。它旨在用于存儲路徑元素的集合,并用于表示整個圖以及parallel-blocks。addStep和parallel方法接受帶有接收者的lambda作為參數。mA028資訊網——每日最新資訊28at.com

帶有接收者的lambda是定義可以訪問特定接收者對象的lambda表達式的一種方式。在函數主體中,傳遞給調用的接收者對象變成了隱式的this,這樣您就可以在沒有任何附加限定符的情況下訪問該接收者對象的成員,或者使用this表達式訪問接收者對象。mA028資訊網——每日最新資訊28at.com

外,如果方法調用的最后一個參數是lambda,可以將lambda放在括號之外。這就是為什么在DSL中我們可以按如下方式編寫代碼mA028資訊網——每日最新資訊28at.com

parallel { addStep {  executor = FINANCE_DEPARTMENT  ... } addStep {  executor = CTO  ... }}

這相當于沒有語法糖的代碼:mA028資訊網——每日最新資訊28at.com

parallel({ this.addStep({  this.executor = FINANCE_DEPARTMENT  ... }) this.addStep({  this.executor = CTO  ... })})

帶接收者的Lambda和括號外的Lambda是Kotlin在處理DSL時特別有用的特性。mA028資訊網——每日最新資訊28at.com

對象聲明

現在不妨看看實體acceptanceacceptance是一個對象。在Kotlin中,對象聲明是定義單例的一種方式,單例指只有一個實例的類。因此,對象聲明同時定義了類及其單個實例。mA028資訊網——每日最新資訊28at.com

“invoke”操作符重載

此外,為accreditation對象重載了invoke操作符invoke操作符是一個可以在類中定義的特殊函數。當您像調用函數一樣調用類的實例時,調用invoke操作符函數。這允許您將對象作為函數來處理,并以類似函數的方式調用它們。mA028資訊網——每日最新資訊28at.com

注意,invoke方法的參數也是帶接收者的lambda。現在我們可以定義驗收路徑:mA028資訊網——每日最新資訊28at.com

val acceptanceRoute = acceptance { addStep {  executor = HEAD_OF_DEPARTMENT  ... } parallel {  addStep {   executor = FINANCE_DEPARTMENT   ...  }  addStep {   executor = CTO   ...  } } addStep {  executor = SECRETARY  ... }}

然后處理它mA028資訊網——每日最新資訊28at.com

val headOfDepartmentStep = acceptanceRoute.elements[0] as StepContext val parallelBlock = acceptanceRoute.elements[1] as AcceptanceContext val ctoStep = parallelBlock.elements[1] as StepContext

五、添加細節

中綴函數

看看這段代碼mA028資訊網——每日最新資訊28at.com

addStep { executor = FINANCE_DEPARTMENT or CTO or CEO ...}

我們可以按以下方式實現這個:mA028資訊網——每日最新資訊28at.com

enum class ExecutorConditionType { EQUALS, OR } data class ExecutorCondition( private val name: String, private val values: Set<ExecutorCondition>, private val type: ExecutorConditionType, ) { infix fun or(another: ExecutorCondition) = ExecutorCondition("or", setOf(this, another), ExecutorConditionType.OR) } val HEAD_OF_DEPARTMENT = ExecutorCondition("HEAD_OF_DEPARTMENT", setOf(), ExecutorConditionType.EQUALS) val FINANCE_DEPARTMENT = ExecutorCondition("FINANCE_DEPARTMENT", setOf(), ExecutorConditionType.EQUALS) val CHIEF = ExecutorCondition("CHIEF", setOf(), ExecutorConditionType.EQUALS) val CTO = ExecutorCondition("CTO", setOf(), ExecutorConditionType.EQUALS) val SECRETARY = ExecutorCondition("SECRETARY", setOf(), ExecutorConditionType.EQUALS) 

ExecutorCondition類允許我們設置幾個可能的任務執行器。在ExecutorCondition中定義了中綴函數or。中綴函數是一種特殊的函數,允許您使用更自然的中綴符號來調用它。mA028資訊網——每日最新資訊28at.com

如果不使用語言的這個特性,我們將不得不這樣寫mA028資訊網——每日最新資訊28at.com

addStep { executor = FINANCE_DEPARTMENT.or(CTO).or(CEO) ... }

中綴函數還用于設置協議的所需狀態和時區時間。mA028資訊網——每日最新資訊28at.com

enum class ProtocolState { formed, signed}class Protocol { var state: ProtocolState? = null infix fun shouldBe(state: ProtocolState) { this.state = state }}enum class TimeZone { ... PST, ...}infix fun String.timezone(tz: TimeZone): ZonedDateTime { val format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z") return ZonedDateTime.parse("$this $tz", format)}

擴展函數

String.timezone是一個擴展函數。在Kotlin中,擴展函數允許您向現有類添加新函數,而無需修改它們的源代碼。當您想要增強無法控制的類的功能時,比如來自標準庫或外部庫的類,這項特性特別有用。mA028資訊網——每日最新資訊28at.com

DSL中的用法mA028資訊網——每日最新資訊28at.com

addStep { ... protocol shouldBe formed dueDate = "2022-12-08 08:00" timezone PST ... }

這里的“2022-12-08 08:00”是接收者對象,針對它調用擴展函數timezone,而PST是參數。使用this關鍵字訪問接收者對象。mA028資訊網——每日最新資訊28at.com

操作符重載

我們在DSL中使用的下一個Kotlin特性是操作符重載。我們已經考慮了invoke操作符的重載。在Kotlin中,您可以重載其他操作符,包括算術操作符。mA028資訊網——每日最新資訊28at.com

addStep { ... +canChange}

這里,一元操作符+被重載。下面是實現這個重載的代碼
mA028資訊網——每日最新資訊28at.com

class StepContext : AcceptanceElement { ... var canChange = ChangePermission() } data class ChangePermission( var canChange: Boolean = true, ) { operator fun unaryPlus() { canChange = true }operator fun unaryMinus() { canChange = false } }

結語

現在我們可以描述DSL上的驗收路徑。然而,應該保護DSL用戶避免可能的錯誤。比如在當前版本中,以下代碼是可以接受的mA028資訊網——每日最新資訊28at.com

val acceptanceRoute = acceptance { addStep { executor = HEAD_OF_DEPARTMENT duration = days(7) protocol shouldBe signed addStep { executor = FINANCE_DEPARTMENT } } }

addStep中的addStep看起來很奇怪,不是?不妨弄清楚為什么這段代碼成功編譯而沒有出現任何錯誤。如上所述,acceptance#invoke和AcceptanceContext#addStep方法接受帶有接收者lambda作為參數,而接收者對象可以通過this關鍵字訪問。所以我們可以像這樣重寫前面的代碼mA028資訊網——每日最新資訊28at.com

val acceptanceRoute = acceptance { this@acceptance.addStep { this@addStep.executor = HEAD_OF_DEPARTMENT this@addStep.duration = days(7) this@addStep.protocol shouldBe signed this@acceptance.addStep { executor = FINANCE_DEPARTMENT } } }

現在可以看到this@acceptance.addStep兩次都被調用了。特別是對于這種情況,Kotlin有一個DslMarker注釋。您可以使用@DslMarker來定義自定義注釋。用相同此類注釋標記的接收者無法對方的內部訪問。mA028資訊網——每日最新資訊28at.com

@DslMarker annotation class AcceptanceDslMarker @AcceptanceDslMarker class AcceptanceContext : AcceptanceElement { ... } @AcceptanceDslMarker class StepContext : AcceptanceElement { ... }

現在mA028資訊網——每日最新資訊28at.com

val acceptanceRoute = acceptance { addStep {  ... addStep { ... } }}

由于錯誤'fun addStep(init: StepContext.() -> Unit): Unit'無法通過隱式接收者在此上下文中調用,上面這段代碼無法編譯。必要時使用顯式接收者。mA028資訊網——每日最新資訊28at.com

原文標題:How to Develop a DSL in Kotlin,作者:Fedor YaremenkomA028資訊網——每日最新資訊28at.com


mA028資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-57843-0.html如何使用Kotlin開發DSL?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 線程池異常黑洞及其防范策略

下一篇: Kafka消息阻塞:拯救面試的八大終極解決方案!

標簽:
  • 熱門焦點
  • 讓我們一起聊聊文件的操作

    文件【1】文件是什么?文件是保存數據的地方,是數據源的一種,比如大家經常使用的word文檔、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存數據,它既可以保
  • 如何通過Python線程池實現異步編程?

    線程池的概念和基本原理線程池是一種并發處理機制,它可以在程序啟動時創建一組線程,并將它們置于等待任務的狀態。當任務到達時,線程池中的某個線程會被喚醒并執行任務,執行完任
  • JavaScript學習 -AES加密算法

    引言在當今數字化時代,前端應用程序扮演著重要角色,用戶的敏感數據經常在前端進行加密和解密操作。然而,這樣的操作在網絡傳輸和存儲中可能會受到惡意攻擊的威脅。為了確保數據
  • 三萬字盤點 Spring 九大核心基礎功能

    大家好,我是三友~~今天來跟大家聊一聊Spring的9大核心基礎功能。話不多說,先上目錄:圖片友情提示,本文過長,建議收藏,嘿嘿嘿!一、資源管理資源管理是Spring的一個核心的基礎功能,不
  • 共享單車的故事講到哪了?

    來源丨??素斀浥c共享充電寶相差不多,共享單車已很久沒有被國內熱點新聞關照到了。除了一再漲價和用戶直呼用不起了。近日多家媒體再發報道稱,成都、天津、鄭州等地多個共享單
  • 小米MIX Fold 3配置細節曝光:搭載領先版驍龍8 Gen2+罕見5倍長焦

    這段時間以來,包括三星、一加、榮耀等等有不少品牌旗下的最新折疊屏旗艦都得到了不少爆料,而小米新一代折疊屏旗艦——小米MIX Fold 3此前也屢屢被傳
  • 引領旗艦級影像能力向中端機普及 OPPO K11 系列發布 1799 元起

    7月25日,OPPO正式發布K系列新品—— OPPO K11 。此次 K11 在中端手機市場長期被忽視的影像板塊發力,突破性地搭載索尼 IMX890 旗艦大底主攝,支持 OIS
  • 聯想小新Pad Pro 12.6將要推出,搭載高通驍龍 870 處理器

    聯想小新Pad Pro 12.6將于秋季新品會上推出,官方按照慣例直接在發布會前給出了機型的所有參數。聯想小新 Pad Pro 12.6 將搭載高通驍龍 870 處理器,重量為 5
  • 外交部:美方應停止在網絡安全問題上不負責任地指責他國

      中國外交部今天(16日)舉行例行記者會。會上,有記者問,美國情報官員稱,他們正在阻攔來自中國以及其他國家的黑客獲取相關科研成果。 中方對此有何評論?對此
Top 主站蜘蛛池模板: 冕宁县| 苏尼特右旗| 台中县| 梧州市| 长阳| 高陵县| 满洲里市| 洛南县| 东源县| 房产| 庆城县| 尤溪县| 浦县| 大理市| 晋城| 湖南省| 长宁县| 保德县| 利津县| 青岛市| 中江县| 马尔康县| 克拉玛依市| 南丰县| 石城县| 大安市| 桦甸市| 科技| 万州区| 钟山县| 亳州市| 中超| 仁布县| 扶沟县| 鄂托克前旗| 沽源县| 漯河市| 河东区| 荆州市| 镇沅| 四子王旗|