成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何設計領域特定語言,實現終極業務抽象?

開發 后端
本文所寫的皆是外部 DSL,即『不同于應用系統主要使用語言』的語言。創建外部 DSL 和創建一種通用目的的編程語言的過程是相似的,它可以是編譯型或者解釋型的。

[[404312]]

在過去的幾年里,我一直從事于各種領域定義語言的設計,包含 unflow、guarding、datum、forming 等。在我剛入門這個領域的時候,我從《領域特定語言》、《編程語言實現模式》 等,一直研究到龍書等。我漸漸掌握了領域特定語言設計的一些技巧,也能快速(相對于過去)設計出一個領域特定語言。

所以,我在想我應該總結一下相關的套路。這樣一來,也可以在未來驗證現在的思路是否正確:

  1. 定義呈現模式。
  2. 提煉領域特定名詞。
  3. 設計關聯關系與語法。
  4. 實現語法解析。
  5. 演進語言的設計。

領域特定語言

領域特定語言(英語:domain-specific language、DSL)指的是專注于某個應用程序領域的計算機語言。

本文所寫的皆是外部 DSL,即『不同于應用系統主要使用語言』的語言。創建外部 DSL 和創建一種通用目的的編程語言的過程是相似的,它可以是編譯型或者解釋型的。

通用目的編程語言的源代碼和外部 DSL 的源代碼之間的主要區別在于,經過編譯的 DSL 通常不會直接產生可執行的程序(但是它確實可以)。大多數情況下,外部 DSL 可以轉換為一種與核心應用程序的操作環境相兼容的資源,也可以轉換為用于構建核心應用的通用目的編程語言。—— Vaughn Vernon

簡單場景下的領域特定語言,只是將特定的源碼轉換為特定的數據結構。如 JSON 便是一種 DSL,在 Java 語言里,需要將它轉換為對應的數據類。復雜場景下的領域特定語言,可以直接編譯為可執行程序。

外部 DSL 的麻煩點在于:

  • 語法設計
  • 語法解析
  • IDE 支持

當然了,它的優點也很明顯:

  1. 讓不懂編程的業務專家(領域專家)快速編寫核心邏輯。
  2. 領域邏輯與具體編程語言無關
  3. 平臺無關。

更多的信息,建議去閱讀《領域特定語言》一書。

定義呈現模式

領域特定語言嘛,從需求上就是對于業務呈現的簡化。根據不同的呈現模式,去解析源碼,得到我們所需要的數據結構。

呈現模式

如下是常見的的領域特定語言的使用模式 [wiki_dsl]:

  • 獨立的工具,如 Makefile
  • 在編譯時或實時轉換為宿主語言
  • 嵌入式領域特定語言
  • ……

可以參見維基百科,我就不再去翻譯了。

[wikidsl]: https://en.wikipedia.org/wiki/Domain-specificlanguage

定義數據結構

從通用語言的編譯過程來看:

  1. 詞法分析器,分析輸入的字符流,得到符號流。
  2. 語法分析,分析符號流,得到語法樹
  3. 語義分析,分析語法樹,得到新的語法樹
  4. 中間代碼生成器,分析語法樹,得到中間表示形式
  5. ……

步驟 1~4,對于通用語言和領域特定語言來說都是極為類似的。唯一擁有區別的是這個中間表示形式,對于領域特定語言來說,我們場景的原因,這里往往是我們所需要的數據結構。

當然了,從某種意義上來說,AST(抽象語法樹)也是一種數據結構,只不過它是一種中間的數據結構。所以,有時候在設計的時候,我就偷懶直接輸出中間表示了。

提煉領域特定名詞

這個環節的過程,實現上和 DDD(領域驅動設計)里的提煉問題域以獲取領域知識是頗為相似的。同樣的這個過程中,通過與領域專家的協作,我們才能獲得更好的領域特定語言。

從用例開始

用例,或譯使用案例、用況,是軟件工程或系統工程中對系統如何反應外界請求的描述,是一種通過用戶的使用場景來獲取需求的技術。

在進行領域驅動設計協作時,我們需要與領域專家理解用戶在這個過程中,進行的一系列操作,以提煉我們所需要的統一語言。而其中的用例能描述達到目標所需的步驟,包含用戶和系統之間的交互。

在創建領域特定語言的時候,這個過程對于我們來說,也是類似的:與領域專家一起協作,從用例開始提煉。它也可以直接由現有的代碼中提煉而來。

從已有用例入手

對于已有系統來說,用例可以由:

與領域專家交流獲取。與領域專家聊天,是我們獲得用例的最好方式。記錄用例,從而獲得關鍵信息。

從現有的代碼中提取。

在 ArchUnit 中提取架構規劃上的設計便是:

  1. classes().that().resideInAPackage("..foo.."
  2. .should().onlyHaveDependentClassesThat().resideInAnyPackage("..source.one..", "..f 

對應的,我們在 Guarding 的設計是:

  1. class(resideIn "..foo..") dependent package(resideIn ["..source.one..""..foo.."

在 Guarding 中設計的是針對主流的編程語言,所以在語法上會盡量與編程語言無關。

提取關鍵字、值、屬性

在獲得了用例作為輸入條件之后,我們就需要從中提取一些關鍵信息,如關鍵字、值、屬性等等。

如下是我在設計 Guarding DSL 時,從 ArchUnit 提取的一小部分關鍵信息:

  1. package: 
  2.   dependOn 
  3.  
  4. class: 
  5.   implement 
  6.   annotation 
  7.   annotatedWith 
  8.  
  9. expression: 
  10.   and 
  11.   or 
  12.   not 
  13.   equals 
  14.   only 

接著,我們就可以依據這些信息,展開它們的關聯設計,進而設計我們的語法。

設計關聯關系與語法

在設計領域特定語言時,我們主要以實現領域中的用例作為目標:

  1. 使用 DSL 描述一個用例
  2. 先不考慮語法實現,實現大部分用例的 DSL 草稿版本
  3. 對齊不同用例 DSL 中的差異
  4. 考慮一些非常規的用例,添加額外的屬性

名詞關系與邏輯設計

領域特定語言,所針對的是特定領域。在特定的領域里,都會使用特定的詞匯來描述相關之間的關系。這個關系,便是我們設計語法的一個關鍵。

如在 Java 語言里,使用: implement、 extends 來表示兩個類之間的關系。而為了表示包之間的關系,則會有: dependent、 resideIn 等等的關系。

實現用例

實現用例并不是一個復雜的過程,只是要符合人類的思維習慣,并盡可能地簡化設計。不過,覺得注意的是,我們應該留下一些證據來告訴未來的自己:我們當時是為什么考慮的。

在設計 DSL 時,我往往會創建一個 sample 文件,以記錄過程中,對于不同的要素的思索。如我在設計 Guarding DSL 里,使用了一個 0.0.1.sample 文本文件,來描述早期版本的語法示例:

  1. # 正則表達式 
  2. package(match("^/app")) endsWith "Connection"
  3.  
  4. package("..home..")::name should not contains(matching("")); 
  5.  
  6. # 簡化的比較 
  7. class::name.len should < 20; 

通過一些注釋來讓自己優化設計。

實現語法解析

這一部分的過程,和我們學習編譯原理時基本是一致的。不過呢,在編寫領域特定語言的時候,我們一般會使用解析器生成器,而不是手寫解析器。

細節設計

設計領域特定語言的時候,在設計語法上的拘束不會像通用語言那么多。所以,自由設計的范圍就大一點,有些內容也不一定需要像編程語言麻煩。諸如于:

  • 分隔符
  • 縮進的處理
  • 語法塊的開始和結束
  • ……

PS:使用類似于編程語言的寫法,對于寫 DSL 的非編程人士來說可能會變成一種困擾。

解析器生成器

經典的 Lex & Yacc 是你可以考慮的范圍,在不同的語言里也有一些相似的實現。

對于我來說,以下是我常用的一些解析器生成器。

  • Antlr。支持主流的語言
  • Peg.js。JavaScript
  • Pest。Rust
  • Lalrpop。Rust

我還是比較習慣用 Antlr,支持的語言較多。我與同事以及開源社區的小伙伴們,在下面的項目中都使用過 Antlr:

  • Coca = Golang + Antlr
  • Unflow = Rust + Antlr
  • Lemonj = JavaScript/TypeScript + Antlr
  • Chapi = Java/Kotlin + Antlr

從使用上它們之間的差距并不大,但是都需要學習成本。

演進語言的設計

最后,讓我們來談談一些有意思的東西,雖說是演進吧,但是,和設計暫時沒有太大的關系。

測試驅動開發

經我大量發現,TDD 是非常適合于編程語言的開發與設計。需求是未知的,易于發生變化的,還需要覆蓋足夠全的場景。

從實踐的層面上來說,主要是有兩種:

  1. 面向語法的測試。即,只讓語法編譯能通過,但是不報錯。
  2. 面向功能的測試。即,驗證某一部分的語法是正確的。
  3. 面向用例的測試。即,驗證符合使用場景。

自動化語言遷移

原先這部分的標題是,向下兼容。但是,我一直覺得向下兼容不是一個好主意。所以呢,我就想了想把在其它領域的經驗搬了過來,于是呢,內容就變成了自動化語言遷移。

在關于版本的遷移上,我覺得 Angular 語言上關于版本的自動化遷移,是值得我們去借鑒的。當然了,采用這種設計的成本非常高,我們需要有一個專門的團隊,使用工具自動化分析舊的系統,并使用工具來自動修改舊的代碼。

其它

文中相關 DSL 鏈接(歡迎加入 Inherd 一起編寫 DSL):

Unflow: https://github.com/inherd/unflow

Guarding: https://github.com/inherd/guarding

Forming: https://github.com/inherd/forming

本文轉載自微信公眾號「phodal」,可以通過以下二維碼關注。轉載本文請聯系phodal公眾號。

 

責任編輯:武曉燕 來源: phodal
相關推薦

2020-03-04 11:20:22

DSL開發領域特定語言

2009-03-12 14:31:15

QCon

2025-05-30 01:33:00

2022-06-17 11:04:47

語法分析器GoyaccAST

2014-09-26 10:00:25

驅動設計DDD領域

2014-09-11 15:05:40

驅動設計驅動開發

2023-08-29 08:57:03

事務腳本架構模式業務場景

2024-08-19 14:06:00

2022-01-28 14:20:53

前端代碼中斷

2023-11-27 15:34:51

大語言模型PubMedBERT

2009-08-27 09:16:48

F#中DSL原型設計

2024-01-22 15:36:54

大語言模型人工智能

2009-11-13 19:54:42

2010-01-25 09:17:01

Visual Stud

2023-02-13 07:04:12

VBC#語言

2018-05-21 07:08:18

行為驅動開發BDD編碼

2024-07-17 08:36:53

2020-04-13 09:54:44

微服務子集存儲

2021-06-30 07:51:09

新項目領域建模

2023-05-29 08:00:00

ChatGPT人工智能機器學習
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产99精品 | 99久久99| 日韩成人高清 | 精品久久99| 日韩欧美一区二区三区四区 | 亚洲精品视频网站在线观看 | 一级毛片网 | 啪啪网页| 亚洲一区二区欧美 | 日韩毛片网| 日本视频一区二区 | 中文字幕综合 | 在线中文字幕亚洲 | 日韩一区二区三区在线观看 | 影音先锋欧美资源 | 精品国产一区二区三区在线观看 | 日韩一区二区福利视频 | 国产aⅴ精品 | 亚洲成人免费视频 | 日韩电影一区二区三区 | 欧美日批| 一区影院 | 四虎影院在线观看av | 欧美日韩国产一区二区三区 | 91 久久 | 国产成人精品视频在线观看 | 国产精品呻吟久久av凹凸 | 色秀网站 | 99久久精品免费看国产四区 | 日韩精品久久 | 欧美性a视频 | 欧美片网站免费 | 日韩中文字幕在线免费 | 欧美在线一区二区三区 | 日本成人福利 | 在线观看中文字幕 | 爱爱视频在线观看 | 中午字幕在线观看 | 色精品 | 欧美在线天堂 | 亚洲女人天堂成人av在线 |