在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

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

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

3天內不再提示

GoF設計模式之迭代器模式

元閏子的邀請 ? 來源:元閏子的邀請 ? 作者:元閏子的邀請 ? 2022-08-01 11:22 ? 次閱讀

上一篇:【Go實現】實踐GoF的23種設計模式:觀察者模式

簡單的分布式應用系統(示例代碼工程):https://github.com/ruanrunxue/Practice-Design-Pattern--Go-Implementation

簡介

有時會遇到這樣的需求,開發一個模塊,用于保存對象;不能用簡單的數組、列表,得是紅黑樹、跳表等較為復雜的數據結構;有時為了提升存儲效率或持久化,還得將對象序列化;但必須給客戶端提供一個易用的 API允許方便地、多種方式地遍歷對象,絲毫不察覺背后的數據結構有多復雜。

346ff082-1143-11ed-ba43-dac502259ad0.jpg

對這樣的 API,很適合使用迭代器模式Iterator Pattern)實現。

GoF 對 迭代器模式 的定義如下:

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

從描述可知,迭代器模式主要用在訪問對象集合的場景,能夠向客戶端隱藏集合的實現細節

Java 的 Collection 家族、C++ 的 STL 標準庫,都是使用迭代器模式的典范,它們為客戶端提供了簡單易用的 API,并且能夠根據業務需要實現自己的迭代器,具備很好的可擴展性。

UML 結構

34994266-1143-11ed-ba43-dac502259ad0.jpg

場景上下文

在簡單的分布式應用系統(示例代碼工程)中,db 模塊用來存儲服務注冊和監控信息,它的主要接口如下:

//demo/db/db.go
packagedb
//Db數據庫抽象接口
typeDbinterface{
CreateTable(t*Table)error
CreateTableIfNotExist(t*Table)error
DeleteTable(tableNamestring)error

Query(tableNamestring,primaryKeyinterface{},resultinterface{})error
Insert(tableNamestring,primaryKeyinterface{},recordinterface{})error
Update(tableNamestring,primaryKeyinterface{},recordinterface{})error
Delete(tableNamestring,primaryKeyinterface{})error

...
}

從增刪查改接口可以看出,它是一個 key-value 數據庫,另外,為了提供類似關系型數據庫的按列查詢能力,我們又抽象出Table對象:

//demo/db/table.go
packagedb
//Table數據表定義
typeTablestruct{
namestring
recordTypereflect.Type
recordsmap[interface{}]record
}

其中,Table底層用map存儲對象數據,但并沒有存儲對象本身,而是從對象轉換而成的recordrecord的實現原理是利用反射機制,將對象的屬性名 field 和屬性值 value 分開存儲,以此支持按列查詢能力(一類對象可以類比為一張表):

//demo/db/record.go
packagedb

typerecordstruct{
primaryKeyinterface{}
fieldsmap[string]int//key為屬性名,value屬性值的索引
values[]interface{}//存儲屬性值
}
//從對象轉換成record
funcrecordFrom(keyinterface{},valueinterface{})(rrecord,eerror){
...//異常處理
vType:=reflect.TypeOf(value)
vVal:=reflect.ValueOf(value)
ifvVal.Type().Kind()==reflect.Pointer{
vType=vType.Elem()
vVal=vVal.Elem()
}
record:=record{
primaryKey:key,
fields:make(map[string]int,vVal.NumField()),
values:make([]interface{},vVal.NumField()),
}
fori:=0;ireturnrecord,nil
}

當然,客戶端并不會察覺 db 模塊背后的復雜機制,它們直接使用的仍是對象:

typetestRegionstruct{
Idint
Namestring
}
funcclient(){
mdb:=db.MemoryDbInstance()
tableName:="testRegion"
table:=NewTable(tableName).WithType(reflect.TypeOf(new(testRegion)))
mdb.CreateTable(table)
mdb.Insert(tableName,"region1",&testRegion{Id:0,Name:"region-1"})
result:=new(testRegion)
mdb.Query(tableName,"region1",result)
}
34ad92b6-1143-11ed-ba43-dac502259ad0.jpg

另外,除了上述按 Key 查詢接口,我們還想提供全表查詢接口,有隨機和有序 2 種表記錄遍歷方式,并且支持客戶端自己擴展遍歷方式。下面使用迭代器模式來實現該需求。

代碼實現

這里并沒有按照標準的 UML 結構去實現,而是結合工廠方法模式來解決公共代碼的復用問題:

34c41cc0-1143-11ed-ba43-dac502259ad0.jpg
//demo/db/table_iterator.go
packagedb

//關鍵點1:定義迭代器抽象接口,允許后續客戶端擴展遍歷方式
//TableIterator表迭代器接口
typeTableIteratorinterface{
HasNext()bool
Next(nextinterface{})error
}

//關鍵點2:定義迭代器接口的實現
//tableIteratorImpl迭代器接口公共實現類
typetableIteratorImplstruct{
//關鍵點3:定義一個集合存儲待遍歷的記錄,這里的記錄已經排序好或者隨機打散
records[]record
//關鍵點4:定義一個cursor游標記錄當前遍歷的位置
cursorint
}

//關鍵點5:在HasNext函數中的判斷是否已經遍歷完所有記錄
func(r*tableIteratorImpl)HasNext()bool{
returnr.cursorlen(r.records)
}

//關鍵點6:在Next函數中取出下一個記錄,并轉換成客戶端期望的對象類型,記得增加cursor
func(r*tableIteratorImpl)Next(nextinterface{})error{
record:=r.records[r.cursor]
r.cursor++
iferr:=record.convertByValue(next);err!=nil{
returnerr
}
returnnil
}

//關鍵點7:通過工廠方法模式,完成不同類型的迭代器對象創建
//TableIteratorFactory表迭代器工廠
typeTableIteratorFactoryinterface{
Create(table*Table)TableIterator
}

//隨機迭代器
typerandomTableIteratorFactorystruct{}
func(r*randomTableIteratorFactory)Create(table*Table)TableIterator{
varrecords[]record
for_,r:=rangetable.records{
records=append(records,r)
}
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(records),func(i,jint){
records[i],records[j]=records[j],records[i]
})
return&tableIteratorImpl{
records:records,
cursor:0,
}
}

//有序迭代器
//Comparator如果i
typeComparatorfunc(i,jinterface{})bool
//sortedTableIteratorFactory根據主鍵進行排序,排序邏輯由Comparator定義
typesortedTableIteratorFactorystruct{
comparatorComparator
}
func(s*sortedTableIteratorFactory)Create(table*Table)TableIterator{
varrecords[]record
for_,r:=rangetable.records{
records=append(records,r)
}
sort.Sort(newRecords(records,s.comparator))
return&tableIteratorImpl{
records:records,
cursor:0,
}
}

最后,為Table對象引入TableIterator

//demo/db/table.go

//Table數據表定義
typeTablestruct{
namestring
recordTypereflect.Type
recordsmap[interface{}]record
//關鍵點8:持有迭代器工廠方法接口
iteratorFactoryTableIteratorFactory//默認使用隨機迭代器
}
//關鍵點9:定義Setter方法,提供迭代器工廠的依賴注入
func(t*Table)WithTableIteratorFactory(iteratorFactoryTableIteratorFactory)*Table{
t.iteratorFactory=iteratorFactory
returnt
}
//關鍵點10:定義創建迭代器的接口,其中調用迭代器工廠完成實例化
func(t*Table)Iterator()TableIterator{
returnt.iteratorFactory.Create(t)
}

客戶端這樣使用:

funcclient(){
table:=NewTable("testRegion").WithType(reflect.TypeOf(new(testRegion))).
WithTableIteratorFactory(NewSortedTableIteratorFactory(regionIdComparator))
iter:=table.Iterator()
foriter.HashNext(){
next:=new(testRegion)
err:=iter.Next(next)
...
}
}

總結實現迭代器模式的幾個關鍵點:

  1. 定義迭代器抽象接口,目的是提供客戶端自擴展能力,通常包含HashNext()Next()兩個方法,上述例子為TableIterator
  2. 定義迭代器接口的實現類,上述例子為tableIteratorImpl,這里主要起到了 Java/C++ 等帶繼承特性語言中,基類的作用,目的是復用代碼。
  3. 在實現類中持有待遍歷的記錄集合,通常是已經排序好或隨機打散后的,上述例子為tableIteratorImpl.records
  4. 在實現類中持有游標值,記錄當前遍歷的位置,上述例子為tableIteratorImpl.cursor
  5. HashNext()方法中判斷是否已經遍歷完所有記錄。
  6. Next()方法中取出下一個記錄,并轉換成客戶端期望的對象類型,取完后增加游標值。
  7. 通過工廠方法模式,完成不同類型的迭代器對象創建,上述例子為TableIteratorFactory接口,以及它的實現,randomTableIteratorFactorysortedTableIteratorFactory
  8. 在待遍歷的對象中,持有迭代器工廠方法接口,上述例子為Table.iteratorFactory
  9. 為對象定義 Setter 方法,提供迭代器工廠的依賴注入,上述例子為Table.WithTableIteratorFactory()方法。
  10. 為對象定義創建迭代器的接口,上述例子為Table.Iterator()方法。

其中,7~9 步是結合工廠方法模式實現時的特有步驟,如果你的迭代器實現中沒有用到工廠方法模式,可以省略這幾步。

擴展

Go 風格的實現

前面的實現,是典型的面向對象風格,下面以隨機迭代器為例,給出一個 Go 風格的實現:

//demo/db/table_iterator_closure.go
packagedb

//關鍵點1:定義HasNext和Next函數類型
typeHasNextfunc()bool
typeNextfunc(interface{})error

//關鍵點2:定義創建迭代器的方法,返回HashNext和Next函數
func(t*Table)ClosureIterator()(HasNext,Next){
varrecords[]record
for_,r:=ranget.records{
records=append(records,r)
}
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(records),func(i,jint){
records[i],records[j]=records[j],records[i]
})
size:=len(records)
cursor:=0
//關鍵點3:在迭代器創建方法定義HasNext和Next的實現邏輯
hasNext:=func()bool{
returncursorfunc(nextinterface{})error{
record:=records[cursor]
cursor++
iferr:=record.convertByValue(next);err!=nil{
returnerr
}
returnnil
}
returnhasNext,next
}

客戶端這樣用:

funcclient(){
table:=NewTable("testRegion").WithType(reflect.TypeOf(new(testRegion))).
WithTableIteratorFactory(NewSortedTableIteratorFactory(regionIdComparator))
hasNext,next:=table.ClosureIterator()
forhasNext(){
result:=new(testRegion)
err:=next(result)
...
}
}

Go 風格的實現,利用了函數閉包的特點,把原本在迭代器實現的邏輯,放到了迭代器創建方法上。相比面向對象風格,省掉了迭代器抽象接口和實現對象的定義,看起來更加的簡潔。

總結幾個實現關鍵點:

  1. 聲明HashNextNext的函數類型,等同于迭代器抽象接口的作用。
  2. 定義迭代器創建方法,返回類型為HashNextNext,上述例子為ClosureIterator()方法。
  3. 在迭代器創建方法內,定義HasNextNext的具體實現,利用函數閉包來傳遞狀態(recordscursor)。

基于 channel 的實現

我們還能基于 Go 語言中的 channel 來實現迭代器模式,因為前文的 db 模塊應用場景并不適用,所以另舉一個簡單的例子:

typeRecordint

func(r*Record)doSomething(){
//...
}

typeComplexCollectionstruct{
records[]Record
}

//關鍵點1:定義迭代器創建方法,返回只能接收的channel類型
func(c*ComplexCollection)Iterator()<-chanRecord{
//關鍵點2:創建一個無緩沖的channel
ch:=make(chanRecord)
//關鍵點3:另起一個goroutine往channel寫入記錄,如果接收端還沒開始接收,會阻塞住
gofunc(){
for_,record:=rangec.records{
ch<-?record
????????}
????//關鍵點4:寫完后,關閉channel
close(ch)
}()
returnch
}

客戶端這樣使用:

funcclient(){
collection:=NewComplexCollection()
//關鍵點5:使用時,直接通過for-range來遍歷channel讀取記錄
forrecord:=rangecollection.Iterator(){
record.doSomething()
}
}

總結實現基于 channel 的迭代器模式的幾個關鍵點:

  1. 定義迭代器創建方法,返回一個只能接收的 channel。
  2. 在迭代器創建方法中,定義一個無緩沖的 channel。
  3. 另起一個 goroutine 往 channel 中寫入記錄。如果接收端沒有接收,會阻塞住。
  4. 寫完后,關閉 channel。
  5. 客戶端使用時,直接通過 for-range 遍歷 channel 讀取記錄即可。

帶有 callback 函數的實現

還可以在創建迭代器時,傳入一個 callback 函數,在迭代器返回記錄前,先調用 callback 函數對記錄進行一些操作。

比如,在基于 channel 的實現例子中,可以增加一個 callback 函數,將每個記錄打印出來:

//關鍵點1:聲明callback函數類型,以Record作為入參
typeCallbackfunc(record*Record)
//關鍵點2:定義具體的callback函數
funcPrintRecord(record*Record){
fmt.Printf("%+v
",record)
}
//關鍵點3:定義以callback函數作為入參的迭代器創建方法
func(c*ComplexCollection)Iterator(callbackCallback)<-chanRecord{
ch:=make(chanRecord)
gofunc(){
for_,record:=rangec.records{
//關鍵點4:遍歷記錄時,調用callback函數作用在每條記錄上
callback(&record)
ch<-?record
????????}
????????close(ch)
}()
returnch
}

funcclient(){
collection:=NewComplexCollection()
//關鍵點5:創建迭代器時,傳入具體的callback函數
forrecord:=rangecollection.Iterator(PrintRecord){
record.doSomething()
}
}

總結實現帶有 callback 的迭代器模式的幾個關鍵點:

  1. 聲明 callback 函數類型,以 Record 作為入參。
  2. 定義具體的 callback 函數,比如上述例子中打印記錄的PrintRecord函數。
  3. 定義迭代器創建方法,以 callback 函數作為入參。
  4. 迭代器內,遍歷記錄時,調用 callback 函數作用在每條記錄上。
  5. 客戶端創建迭代器時,傳入具體的 callback 函數。

典型應用場景

  • 對象集合/存儲類模塊,并希望向客戶端隱藏模塊背后的復雜數據結構。

  • 希望支持客戶端自擴展多種遍歷方式。

優缺點

優點

  • 隱藏模塊背后復雜的實現機制,為客戶端提供一個簡單易用的接口

  • 支持擴展多種遍歷方式,具備較強的可擴展性,符合開閉原則

  • 遍歷算法和數據存儲分離,符合單一職責原則

缺點

  • 容易濫用,比如給簡單的集合類型實現迭代器接口,反而使代碼更復雜。
  • 相比于直接遍歷集合,迭代器效率要更低一些,因為涉及到更多對象的創建,以及可能的對象拷貝。
  • 需要時刻注意在迭代器遍歷過程中,由原始集合發生變更引發的并發問題。一種解決方法是,在創建迭代器時,拷貝一份原始數據(TableIterator就這么實現),但存在效率低、內存占用大的問題。

與其他模式的關聯

迭代器模式通常會與工廠方法模式一起使用,如前文實現。

文章配圖

可以在用Keynote畫出手繪風格的配圖中找到文章的繪圖方法。

審核編輯:湯梓紅


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

    關注

    0

    文章

    53

    瀏覽量

    8645
  • 迭代器
    +關注

    關注

    0

    文章

    44

    瀏覽量

    4329

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

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

收藏 人收藏

    評論

    相關推薦

    談談Python 中的迭代模式

    年,Design Patterns - Elements of Reusable Object-Oriented Software)中,它提出了23種設計模式迭代模式就是其中的一種
    發表于 11-23 13:10 ?805次閱讀
    談談Python 中的<b class='flag-5'>迭代</b><b class='flag-5'>器</b><b class='flag-5'>模式</b>

    關于國產MCU GOF32F103C8T6 軟硬件通用

    GOF32F103X8和GOF32F103XB標準型MCU系列使用高性能的ARM Cortex-M3 32 位的RISC內核,工作頻率為72MHz,內置高速存儲(高達128K字節的閃存和20K字節
    發表于 04-19 09:50

    Command模式與動態語言

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

    Modbus ASCII 模式通訊程序

    C語言編寫的臺達變頻Modbus ASCII 模式通訊程序
    發表于 12-02 10:25 ?6次下載

    Proteus定時_計數0的CTC模式應用

    Proteus定時_計數0的CTC模式應用,很好的Proteus了,快來下載不學習吧。
    發表于 04-18 15:34 ?0次下載

    Proteus定時_計數0的快速PWM模式應用

    Proteus定時_計數0的快速PWM模式應用,很好的Proteus了,快來下載不學習吧
    發表于 04-18 15:34 ?0次下載

    Proteus定時_計數1的CTC模式應用

    Proteus定時_計數1的CTC模式應用,很好的Proteus了,快來下載不學習吧。
    發表于 04-18 15:34 ?0次下載

    Proteus定時_計數2的CTC模式應用

    Proteus定時_計數2的CTC模式應用,很好的Proteus了,快來下載不學習吧。
    發表于 04-18 15:34 ?0次下載

    算法與數據結構——迭代模式

    第三章為算法與數據結構,本文為3.4 迭代模式
    的頭像 發表于 09-20 17:09 ?4981次閱讀
    算法與數據結構——<b class='flag-5'>迭代</b><b class='flag-5'>器</b><b class='flag-5'>模式</b>

    嵌入式軟件設計設計模式

    文章目錄前言1.設計模式適配器模式2.設計模式單例模式3.設計
    發表于 10-21 11:07 ?9次下載
    嵌入式軟件設計<b class='flag-5'>之</b>設計<b class='flag-5'>模式</b>

    GoF設計模式訪問者模式

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

    GoF設計模式代理模式

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

    迭代模式在UVM中的應用有哪些

    行為型設計模式數量較多,上一篇介紹了模板模式和策略模式,下面對迭代模式進行介紹,挖掘其在UVM中的應用。
    的頭像 發表于 08-14 17:15 ?635次閱讀
    <b class='flag-5'>迭代</b><b class='flag-5'>模式</b>在UVM中的應用有哪些

    實踐GoF的23種設計模式:備忘錄模式

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

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

    解釋模式(Interpreter Pattern)應該是 GoF 的 23 種設計模式中使用頻率最少的一種了,它的應用場景較為局限。
    的頭像 發表于 04-01 11:01 ?738次閱讀
    實踐<b class='flag-5'>GoF</b>的23種設計<b class='flag-5'>模式</b>:解釋<b class='flag-5'>器</b><b class='flag-5'>模式</b>
    主站蜘蛛池模板: 性欧美精品| 中年艳妇乱小玩| 久久深夜福利| 精品久久免费观看| 1024免费看片| 又粗又长又色又爽视频| 国卡一卡二卡三免费网站| 成人网18免费下| 成人小视频在线| 一级片免费在线观看视频| 五月天久久婷婷| 不卡中文字幕在线| 一级免费看片| 视频综合网| 91极品女神私人尤物在线播放| h网站在线观看| 中国一级毛片aaa片| 四虎影院国产| 看毛片的网站| 337p亚洲精品色噜噜狠狠| 欧美一级黄色片在线观看| h网站免费在线观看| www.一区二区| 日本污视频网站| 国产免费成人在线视频| 天天舔天天| 成人午夜网站| 狠狠色噜噜狠狠狠狠2018| 天堂网在线www| 极品啪啪| 天天操天天摸天天射| 日本68xxxxxxxxx老师| 99热.com| 欧美h视频| 亚洲午夜视频在线| 国产精品爽爽影院在线| 啪啪网站色大全免费| 色婷婷5月精品久久久久| 韩国a级床戏大尺度在线观看| 午夜视频免费国产在线| 永久免费看的啪啪网站|