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

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

如何使用Kotlin開發DSL?

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

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

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

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

一、領域特定語言的定義

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

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

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

如果您檢查字符串是否符合通用語言(比如Java中的指定格式,您將得到以下代碼X5e28資訊網——每日最新資訊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(); }

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

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

二、DSL的類型

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

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

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

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

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

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

三、一個真實的例子

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

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

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

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

外部DSLX5e28資訊網——每日最新資訊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。X5e28資訊網——每日最新資訊28at.com

四、實施DSL的基本結構

不妨開始開發一個對象模型X5e28資訊網——每日最新資訊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作為參數。X5e28資訊網——每日最新資訊28at.com

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

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

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

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

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

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

對象聲明

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

“invoke”操作符重載

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

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

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

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

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

五、添加細節

中綴函數

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

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

我們可以按以下方式實現這個:X5e28資訊網——每日最新資訊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。中綴函數是一種特殊的函數,允許您使用更自然的中綴符號來調用它。X5e28資訊網——每日最新資訊28at.com

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

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

中綴函數還用于設置協議的所需狀態和時區時間。X5e28資訊網——每日最新資訊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中,擴展函數允許您向現有類添加新函數,而無需修改它們的源代碼。當您想要增強無法控制的類的功能時,比如來自標準庫或外部庫的類,這項特性特別有用。X5e28資訊網——每日最新資訊28at.com

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

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

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

操作符重載

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

addStep { ... +canChange}

這里,一元操作符+被重載。下面是實現這個重載的代碼
X5e28資訊網——每日最新資訊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用戶避免可能的錯誤。比如在當前版本中,以下代碼是可以接受的X5e28資訊網——每日最新資訊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關鍵字訪問。所以我們可以像這樣重寫前面的代碼X5e28資訊網——每日最新資訊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來定義自定義注釋。用相同此類注釋標記的接收者無法對方的內部訪問。X5e28資訊網——每日最新資訊28at.com

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

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

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

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

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


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

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

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

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

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

標簽:
  • 熱門焦點
  • MIX Fold3包裝盒泄露 新機本月登場

    小米的全新折疊屏旗艦MIX Fold3將于本月發布,近日該機的真機包裝盒在網上泄露。從圖上來看,新的MIX Fold3包裝盒在外觀設計方面延續了之前的方案,變化不大,這也是目前小米旗艦
  • 盧偉冰長文解析K60至尊版 對Redmi有著里程碑式的意義

    在今天的Redmi后性能時代戰略發布會結束之后,Redmi總經理盧偉冰又帶來了一篇長文,詳解了為什么 Redmi 要開啟后性能時代?為什么選擇和 MediaTek、Pixelworks 深度合作?以及后性
  • 十個可以手動編寫的 JavaScript 數組 API

    JavaScript 中有很多API,使用得當,會很方便,省力不少。 你知道它的原理嗎? 今天這篇文章,我們將對它們進行一次小總結?,F在開始吧。1.forEach()forEach()用于遍歷數組接收一參
  • 微信語音大揭秘:為什么禁止轉發?

    大家好,我是你們的小米。今天,我要和大家聊一個有趣的話題:為什么微信語音不可以轉發?這是一個我們經常在日常使用中遇到的問題,也是一個讓很多人好奇的問題。讓我們一起來揭開這
  • 自律,給不了Keep自由!

    來源 | 互聯網品牌官作者 | 李大為編排 | 又耳 審核 | 谷曉輝自律能不能給用戶自由暫時不好說,但大概率不能給Keep自由。近日,全球最大的在線健身平臺Keep正式登陸港交所,努力
  • 一條抖音4億人圍觀 ! 這家MCN比無憂傳媒還野

    作者:Hiu 來源:互聯網品牌官01 擦邊少女空降熱搜,幕后推手曝光被網友譽為&ldquo;純欲天花板&rdquo;的女網紅井川里予,近期因為一組哥特風照片登上熱搜,引發了一場互聯網世界關于
  • iQOO 11S屏幕細節公布:首發三星2K E6全感屏 安卓最好的直屏手機

    日前iQOO手機官方宣布,新一代電競旗艦iQOO 11S將會在7月4日19:00正式與大家見面。隨著發布時間的日益臨近,官方關于該機的預熱也更加密集,截至目前已
  • iQOO Neo8系列新品發布會

    旗艦雙芯 更強更Pro
  • OPPO K11采用全方位護眼屏:三大護眼能力減輕視覺疲勞

    日前OPPO官方宣布,全新的OPPO K11將于7月25日正式發布,將主打旗艦影像,和同檔位競品相比,其最大的賣點就是將配備索尼IMX890主攝,堪稱是2000檔位影像表
Top 主站蜘蛛池模板: 临桂县| 乡宁县| 灵丘县| 南充市| 宿迁市| 鹰潭市| 兴宁市| 乌拉特前旗| 白山市| 紫阳县| 富顺县| 贵港市| 拜泉县| 兴业县| 高州市| 逊克县| 嘉鱼县| 大方县| 北宁市| 景德镇市| 拉孜县| 嘉义市| 桂林市| 大悟县| 海伦市| 乌拉特前旗| 罗田县| 五原县| 南安市| 五莲县| 和平县| 靖宇县| 留坝县| 同仁县| 丽江市| 石狮市| 习水县| 富裕县| 叙永县| 锦屏县| 昌吉市|