<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

實踐GoF的23種設計模式:解釋器模式

元閏子的邀請 ? 來源:元閏子的邀請 ? 2024-04-01 11:01 ? 次閱讀

簡介

解釋器模式(Interpreter Pattern)應該是 GoF 的 23 種設計模式中使用頻率最少的一種了,它的應用場景較為局限。

GoF 對它的定義如下:

Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

從定義可以看出,解釋器模式主要運用于簡單的語法解析場景,比如簡單的領域特定語言(DSL)。舉個例子,我們可以使用解析器模式來對“1+2+3-4+1”這樣的文本表達式完成解析,并得到最終答案“3”。

解釋器模式的整體思想是分而治之,每一個語法規則都使用一個類或者結構體(我們稱之為 Rule Struct)來定義,它們相互獨立,比如前一個例子中,“+” 和 “-” 都各自定義為一個 Rule Struct。因此,解釋器模式的可擴展性很好。

通常,我們還能使用抽象語法樹(Abstract Syntax Tree,AST)來直觀地表示待解釋的表達式,比如“1+2+3-4+1”可以表示成這樣:

86dd8eb8-e920-11ee-a297-92fbcf53809c.png

UML 結構

87240654-e920-11ee-a297-92fbcf53809c.png

解釋器模式通常有 4 種角色:

Context:解釋上下文,包含了解釋語法需要的所有信息,它是的生命周期貫穿整個解釋過程,是一個全局對象。

AbstractExpression:聲明了解釋語法的方法,通常只有Interpret(*Context)一個方法。

TerminalExpression:實現了 AbstractExpression 接口,定義了終結表達式的解析邏輯。終結表達式在抽象語法樹中作為葉子節點。

NonterminalExpression:實現了 AbstractExpression 接口,定義了非終結表達式的解析邏輯。在抽象語法樹中,除了葉子節點,其他節點都是非終結表達式。NonterminalExpression 通常會比 TerminalExpression 更復雜一些。

場景上下文

在簡單的分布式應用系統(示例代碼工程)中,db 模塊用來存儲服務注冊信息和系統監控數據,它是一個 key-value 數據庫。為了更高的易用性,它支持簡單的 SQL 查詢功能。用戶在終端控制臺上可以通過 SQL 語句來查詢數據庫中的數據:

874b975a-e920-11ee-a297-92fbcf53809c.png

簡單起見,我們實現的 SQL 固定為select xxx,xxx,xxx from xxx where xxx=xxx;的形式,為此,我們要實現 3 個 TerminalExpression,即SelectExpression、FromExpression和WhereExpression,分別解釋 select 語句、from 語句、where 語句;以及 1 個 NonterminalExpression,即CompoundExpression,用來解釋整個 SQL 語句。

876b987a-e920-11ee-a297-92fbcf53809c.png

代碼實現

//demo/db/sql.go
packagedb

//關鍵點1:定義Context結構體/類,這里是SqlContext,里面存放解析過程所需的狀態和數據,以及結果數據
//SqlContextSQL解析器上下文,保存各個表達式解析的中間結果
//當前只支持基于主鍵的查詢SQL語句
typeSqlContextstruct{
tableNamestring
fields[]string
primaryKeyinterface{}
}

...

//關鍵點2:定義AbstractExpression接口,這里是SqlExpression,其中Interpret方法以Context作為入參
//SqlExpressionSql表達式抽象接口,每個詞、符號和句子都屬于表達式
typeSqlExpressioninterface{
Interpret(ctx*SqlContext)error
}

//關鍵點3:定義TerminalExpression,實現AbstractExpression接口,這里是SelectExpression、FromExpression和WhereExpression
//SelectExpressionselect語句解析邏輯,select關鍵字后面跟的為field,以,分割,比如selectId,name
typeSelectExpressionstruct{
fieldsstring
}

func(s*SelectExpression)Interpret(ctx*SqlContext)error{
fields:=strings.Split(s.fields,",")
iflen(fields)==0{
returnErrSqlInvalidGrammar
}
//關鍵點4:在解析過程中將狀態或者結果數據存儲到Context里面
ctx.SetFields(fields)
returnnil
}

//FromExpressionfrom語句解析邏輯,from關鍵字后面跟的為表名,比如fromregionTable1
typeFromExpressionstruct{
tableNamestring
}

func(f*FromExpression)Interpret(ctx*SqlContext)error{
iff.tableName==""{
returnErrSqlInvalidGrammar
}
ctx.SetTableName(f.tableName)
returnnil
}

//WhereExpressionwhere語句解析邏輯,where關鍵字后面跟的是主鍵過濾條件,比如whereid='1'
typeWhereExpressionstruct{
conditionstring
}

func(w*WhereExpression)Interpret(ctx*SqlContext)error{
vals:=strings.Split(w.condition,"=")
iflen(vals)!=2{
returnErrSqlInvalidGrammar
}
ifstrings.Contains(vals[1],"'"){
ctx.SetPrimaryKey(strings.Trim(vals[1],"'"))
returnnil
}
ifval,err:=strconv.Atoi(vals[1]);err==nil{
ctx.SetPrimaryKey(val)
returnnil
}
returnErrSqlInvalidGrammar
}

//關鍵點5:實現NonterminalExpression,這里是CompoundExpression,它在解釋過程中會引用到TerminalExpression,可以將TerminalExpression作為成員變量,也可以在Interpret方法中直接創建新對象。
//CompoundExpressionSQL語句解釋器,SQL固定為selectxxx,xxx,xxxfromxxxwherexxx=xxx;的固定格式
//例子:selectregionIdfromregionTablewhereregionId=1
typeCompoundExpressionstruct{
sqlstring
}

func(c*CompoundExpression)Interpret(ctx*SqlContext)error{
childs:=strings.Split(c.sql,"")
iflen(childs)!=6{
returnErrSqlInvalidGrammar
}
//關鍵點6:在NonterminalExpression的Interpret方法中,調用TerminalExpression的Interpret方法完成對語句的解釋。
fori:=0;i

客戶端這么使用:

//demo/db/memory_db.go
packagedb

//memoryDb內存數據庫
typememoryDbstruct{
tablessync.Map//key為tableName,value為table
}

...

func(m*memoryDb)ExecSql(sqlstring)(*SqlResult,error){
ctx:=NewSqlContext()
express:=&CompoundExpression{sql:sql}
iferr:=express.Interpret(ctx);err!=nil{
returnnil,ErrSqlInvalidGrammar
}
//關鍵點7:解釋成功后,從Context中獲取解釋結果信息
table,ok:=m.tables.Load(ctx.TableName())
if!ok{
returnnil,ErrTableNotExist
}
record,ok:=table.(*Table).records[ctx.PrimaryKey()]
if!ok{
returnnil,ErrRecordNotFound
}
result:=NewSqlResult()
for_,f:=rangectx.Fields(){
field:=strings.ToLower(f)
ifidx,ok:=table.(*Table).metadata[field];ok{
result.Add(field,record.values[idx])
}
}
returnresult,nil
}

總結實現解釋器模式的幾個關鍵點:

定義 Context 結構體/類,這里是SqlContext,里面存放解釋過程所需的狀態和數據,也會存儲解釋結果。

定義 AbstractExpression 接口,這里是SqlExpression,其中Interpret方法以 Context 作為入參。

定義 TerminalExpression 結構體,并實現 AbstractExpression 接口,這里是SelectExpression、FromExpression和WhereExpression。

將Interpret方法解釋過程中產生的過程狀態、數據存儲在 Context 上,使得其他 Expression 在解釋過程中能夠訪問。

實現 NonterminalExpression,這里是CompoundExpression,它在解釋過程中會引用到 TerminalExpression,可以把 TerminalExpression 作為成員變量,也可以在 Interpret 方法中直接創建新對象。

在 NonterminalExpression 的 Interpret 方法中,調用 TerminalExpression 的 Interpret 方法完成對語句的解釋。這里是CompoundExpression.Interpret調用SelectExpression.Interpret、FromExpression.Interpret和WhereExpression.Interpret完成對 SQL 的解釋。

解釋成功后,從 Context 中獲取解釋結果。

擴展

領域特定語言 DSL

在前文介紹解釋器模式時有提到,它常用于對領域特定語言 DSL 的解釋場景,那么什么是 DSL 呢?下面我們將簡單介紹一下。

維基百科對 DSL 的定義如下:

Adomain-specific language(DSL) is a computer language specialized to a particular application domain.

可見,DSL 是針對特定領域的一種計算機語言,與之相對的是 GPL,General Purpose Language,即通用編程語言。我們常用的 C/C++,Java,Go 等都屬于 GPL 的范疇。

DSL 又可細分成 2 類:

External DSL:此類 DSL 擁有獨立的語法以及解釋器,比如 CSS 用于定義 Web 網頁的樣式和布局、SQL 用于數據查詢、XML 和 YAML 用于配置管理,它們都是典型的 External DSL。

#ExternalDSL舉例,SQL
selectid,namefromregionswhereid=‘1’;

Internal DSL:此類 DSL 構建與 GPL 之上,比如流式接口 fluent interface、單元測試中的 Mock 庫,它們可以提升 GPL 的易用性和易理解性。

//InternalDSL,Java中的Mockito庫
Mockito.when(mockDemo.isTrue()).thenReturn(1);

Martin Fowler 大神專門寫了一本書《領域特定語言》來介紹 DSL,更多詳細、專業的知識請移步這里。

典型應用場景

簡單的語法解析。解釋器模式的運用場景較為單一,主要運用于簡單的語法解析場景,比如簡單的領域特定語言(DSL)。

優缺點

優點

易于擴展。前文提到,使用解釋器模式進行語法解釋時,每種語法規則都會有對應的 Expression 結構體/類。因此,新增一種語法規則會非常的容易;類似地,改變一種已有的語法規則的解釋方式也是很容易,單點改動即可。

缺點

不適用于復雜的語法解釋。當語法過于復雜時,Expression 結構體/類的數量將會變得很多,從而難以維護。

與其他模式的關聯

解釋器模式通常與組合模式(Composite Pattern)結合在一起使用,UML 結構圖中的 NonterminalExpression 和 AbstractExpression 的就是組合關系。

另外,解釋器模式這種分而治之的方法,與狀態模式(State Pattern)中每種狀態處理各種的邏輯很是類似。



審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • DSL
    DSL
    +關注

    關注

    2

    文章

    57

    瀏覽量

    38211
  • 數據存儲
    +關注

    關注

    5

    文章

    921

    瀏覽量

    50663
  • SQL
    SQL
    +關注

    關注

    1

    文章

    740

    瀏覽量

    43530
  • UML
    UML
    +關注

    關注

    0

    文章

    122

    瀏覽量

    30773
  • 解釋器
    +關注

    關注

    0

    文章

    98

    瀏覽量

    6431

原文標題:【Go實現】實踐GoF的23種設計模式:解釋器模式

文章出處:【微信號:yuanrunzi,微信公眾號:元閏子的邀請】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    關于國產MCU GOF32F103C8T6 軟硬件通用

    +105℃的擴展溫度范圍,一系列的省電模式保證低功耗應用的要求。GOF32F103X8和GOF32F103XB標準型系列產品提供包括從36腳至100腳的4不同封裝形式:根據不同的封裝
    發表于 04-19 09:50

    GPIO的8工作模式要點解釋

    STM32八IO口模式學習要點一、GPIO的8中工作模式要點解釋。----開漏輸出和推挽輸出的概念需要理解清楚。浮空,顧名思義就是浮在空中,上面用繩子一拉就上去了,下面用繩子一拉就沉
    發表于 08-05 08:16

    MCU系統接口12模式的實現細節解釋

    的4.1。這里不再累述。接下來對這12模式的實現細節做進一步解釋。1、8080-I Series Parallel interfaceILI9341可以通過MCU 8080-I串并行接口(分別為8位/9位/16位/18位)的方
    發表于 02-11 06:04

    23基本的設計模式總結

    一樣。?提到設計模式,不得不感謝GoF(***,四人組),他們1995年出版的《設計模式》一書,第一次將設計模式提升到理論高度,并將之規范化。書中一共總結了
    發表于 03-01 06:08

    Command模式與動態語言

    Gof的設計模式中,有一個模式引起的爭議比較大,有很多人甚至認為這個模式應該排除在OO模式之外,原因在于它不具有OO的特性
    發表于 06-22 10:20 ?857次閱讀
    Command<b class='flag-5'>模式</b>與動態語言

    C#23種設計模式【完整】

    C#23種設計模式
    發表于 08-21 17:38 ?54次下載

    23種java設計模式

    JAVA的設計模式經前人總結可以分為23種 設計模式根據使用類型可以分為三種: 1、創建模式: 2、結構模式: 3、行為
    發表于 09-23 15:17 ?0次下載

    GoF設計模式之訪問者模式

    訪問者模式的目的是,解耦數據結構和算法,使得系統能夠在不改變現有代碼結構的基礎上,為對象新增一種新的操作。
    的頭像 發表于 10-08 11:05 ?523次閱讀

    GoF設計模式之代理模式

    它是一個使用率非常高的設計模式,在現實生活中,也是很常見。比如,演唱會門票黃牛。假設你需要看一場演唱會,但官網上門票已經售罄,于是就當天到現場通過黃牛高價買了一張。在這個例子中,黃牛就相當于演唱會門票的代理,在正式渠道無法購買門票的情況下,你通過代理完成了該目標。
    的頭像 發表于 10-17 09:45 ?679次閱讀

    設計模式最佳實踐探索—策略模式

    根據不同的應用場景與意圖,設計模式主要分為創建型模式、結構型模式和行為型模式三類。本文主要探索行為型模式中的策略
    的頭像 發表于 10-31 14:24 ?743次閱讀

    嵌入式軟件的設計模式(上)

    一般常見的是四人幫模式GOF23種設計模式,是偏向于可復用的面向對象的軟件,并不能很完美的契合嵌入式軟件,因為嵌入式C語言是結構化的語言,與硬件關聯。雖然也可強制封裝結構體實現類似
    的頭像 發表于 01-20 11:32 ?958次閱讀
    嵌入式軟件的設計<b class='flag-5'>模式</b>(上)

    實踐GoF23種設計模式:命令模式簡介

    因此,我們需要對請求進行抽象,將上下文信息封裝到請求對象里,這其實就是命令模式,而該請求對象就是 Command。
    的頭像 發表于 01-13 16:36 ?556次閱讀

    實踐GoF23種設計模式:備忘錄模式

    相對于代理模式、工廠模式等設計模式,備忘錄模式(Memento)在我們日常開發中出鏡率并不高,除了應用場景的限制之外,另一個原因,可能是備忘錄模式
    的頭像 發表于 11-25 09:05 ?309次閱讀
    <b class='flag-5'>實踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b>種設計<b class='flag-5'>模式</b>:備忘錄<b class='flag-5'>模式</b>

    實踐GoF23種設計模式:適配器模式

    適配器模式所做的就是將一個接口 Adaptee,通過適配器 Adapter 轉換成 Client 所期望的另一個接口 Target 來使用,實現原理也很簡單,就是 Adapter 通過實現 Target接口,并在對應的方法中調用 Adaptee 的接口實現。
    的頭像 發表于 12-10 14:00 ?331次閱讀
    <b class='flag-5'>實踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b>種設計<b class='flag-5'>模式</b>:適配器<b class='flag-5'>模式</b>

    實踐GoF23種設計模式實現:橋接模式

    也即,將抽象部分和實現部分進行解耦,使得它們能夠各自往獨立的方向變化。
    的頭像 發表于 04-14 09:30 ?218次閱讀
    <b class='flag-5'>實踐</b><b class='flag-5'>GoF</b>的<b class='flag-5'>23</b>種設計<b class='flag-5'>模式</b>實現:橋接<b class='flag-5'>模式</b>
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>