Go設(shè)計(jì)模式之訪問者模式講解和代碼示例
Go 訪問者模式講解和代碼示例
訪問者是一種行為設(shè)計(jì)模式, 允許你在不修改已有代碼的情況下向已有類層次結(jié)構(gòu)中增加新的行為。
閱讀我們的文章訪問者和雙分派以了解為什么不能通過方法重載來簡單地替換訪問者。
概念示例
訪問者模式允許你在結(jié)構(gòu)體中添加行為, 而又不會(huì)對(duì)結(jié)構(gòu)體造成實(shí)際變更。 假設(shè)你是一個(gè)代碼庫的維護(hù)者, 代碼庫中包含不同的形狀結(jié)構(gòu)體, 如:
- 方形
- 圓形
- 三角形
上述每個(gè)形狀結(jié)構(gòu)體都實(shí)現(xiàn)了通用形狀接口。
在公司員工開始使用你維護(hù)的代碼庫時(shí), 你就會(huì)被各種功能請(qǐng)求給淹沒。 讓我們來看看其中比較簡單的請(qǐng)求: 有個(gè)團(tuán)隊(duì)請(qǐng)求你在形狀結(jié)構(gòu)體中添加 getArea
獲取面積行為。
解決這一問題的辦法有很多。
第一個(gè)選項(xiàng)便是將 getArea
方法直接添加至形狀接口, 然后在各個(gè)形狀結(jié)構(gòu)體中進(jìn)行實(shí)現(xiàn)。 這似乎是比較好的解決方案, 但其代價(jià)也比較高。 作為代碼庫的管理員, 相信你也不想在每次有人要求添加另外一種行為時(shí)就去冒著風(fēng)險(xiǎn)改動(dòng)自己的寶貝代碼。 不過, 你也一定想讓其他團(tuán)隊(duì)的人還是用一用自己的代碼庫。
第二個(gè)選項(xiàng)是請(qǐng)求功能的團(tuán)隊(duì)自行實(shí)現(xiàn)行為。 然而這并不總是可行, 因?yàn)樾袨榭赡軙?huì)依賴于私有代碼。
第三個(gè)方法就是使用訪問者模式來解決上述問題。 首先定義一個(gè)如下訪問者接口:
type visitor interface { visitForSquare(square) visitForCircle(circle) visitForTriangle(triangle) }
我們可以使用 visitForSquare(square)
、 visitForCircle(circle)
以及 visitForTriangle(triangle)
函數(shù)來為方形、 圓形以及三角形添加相應(yīng)的功能。
你可能在想, 為什么我們不再訪問者接口里面使用單一的 visit(shape)
方法呢? 這是因?yàn)?Go 語言不支持方法重載, 所以你無法以相同名稱、 不同參數(shù)的方式來使用方法。
好了, 第二項(xiàng)重要的工作是將 accept
接受方法添加至形狀接口中。
func accept(v visitor)
所有形狀結(jié)構(gòu)體都需要定義此方法, 類似于:
func (obj *square) accept(v visitor){ v.visitForSquare(obj) }
等等, 我剛才是不是提到過, 我們并不想修改現(xiàn)有的形狀結(jié)構(gòu)體? 很不幸, 在使用訪問者模式時(shí), 我們必須要修改形狀結(jié)構(gòu)體。 但這樣的修改只需要進(jìn)行一次。
如果添加任何其他行為, 比如 getNumSides
獲取邊數(shù)和 getMiddleCoordinates
獲取中點(diǎn)坐標(biāo) , 我們將使用相同的 accept(v visitor)
函數(shù), 而無需對(duì)形狀結(jié)構(gòu)體進(jìn)行進(jìn)一步的修改。
最后, 形狀結(jié)構(gòu)體只需要修改一次, 并且所有未來針對(duì)不同行為的請(qǐng)求都可以使用相同的 accept 函數(shù)來進(jìn)行處理。 如果團(tuán)隊(duì)成員請(qǐng)求 getArea
行為, 我們只需簡單地定義訪問者接口的具體實(shí)現(xiàn), 并在其中編寫面積的計(jì)算邏輯即可。
shape.go: 元件
package main // 形狀結(jié)構(gòu)體 type Shape interface { getType() string accept(Visitor) }
square.go: 具體元件
package main type Square struct { side int } func (s *Square) accept(v Visitor) { v.visitForSquare(s) } func (s *Square) getType() string { return "Square" }
circle.go: 具體元件
package main type Circle struct { radius int } func (c *Circle) accept(v Visitor) { v.visitForCircle(c) } func (c *Circle) getType() string { return "Circle" }
rectangle.go: 具體元件
package main type Rectangle struct { l int b int } func (t *Rectangle) accept(v Visitor) { v.visitForrectangle(t) } func (t *Rectangle) getType() string { return "rectangle" }
visitor.go: 訪問者
package main type Visitor interface { visitForSquare(*Square) visitForCircle(*Circle) visitForrectangle(*Rectangle) }
areaCalculator.go: 具體訪問者
package main import "fmt" type AreaCalculator struct { area int } func (a *AreaCalculator) visitForSquare(s *Square) { fmt.Println("calculating area for square") } func (a *AreaCalculator) visitForCircle(s *Circle) { fmt.Println("Calculating area for circle") } func (a *AreaCalculator) visitForrectangle(s *Rectangle) { fmt.Println("Calculating area for rectangle") }
middleCoordinates.go: 具體訪問者
package main import "fmt" type MiddleCoordinates struct { x int y int } func (a *MiddleCoordinates) visitForSquare(s *Square) { fmt.Println("Calculating middle point coordinates for square") } func (a *MiddleCoordinates) visitForCircle(c *Circle) { fmt.Println("Calculating middle point coordinates for circle") } func (a *MiddleCoordinates) visitForrectangle(t *Rectangle) { fmt.Println("Calculating middle point coordinates for rectangle") }
main.go: 客戶端代碼
package main import "fmt" func main() { square := &Square{side: 2} circle := &Circle{radius: 3} rectangle := &Rectangle{l: 2, b: 3} areaCalculator := &AreaCalculator{} square.accept(areaCalculator) circle.accept(areaCalculator) rectangle.accept(areaCalculator) fmt.Println() middleCoordinates := &MiddleCoordinates{} square.accept(middleCoordinates) circle.accept(middleCoordinates) rectangle.accept(middleCoordinates) }
output.txt: 執(zhí)行結(jié)果
calculating area for square
Calculating area for circle
Calculating area for rectangleCalculating middle point coordinates for square
Calculating middle point coordinates for circle
到此這篇關(guān)于Go設(shè)計(jì)模式之訪問者模式講解和代碼示例的文章就介紹到這了,更多相關(guān)Go訪問者模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Go如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化
這篇文章主要為大家詳細(xì)介紹了Go語言中是如何優(yōu)雅的對(duì)時(shí)間進(jìn)行格式化的,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-06-06GO使用阿里云,解決go get下載項(xiàng)目慢或無法下載的情況
這篇文章主要介紹了GO使用阿里云,解決go get下載項(xiàng)目慢或無法下載的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01基于golang的簡單分布式延時(shí)隊(duì)列服務(wù)的實(shí)現(xiàn)
這篇文章主要介紹了基于golang的簡單分布式延時(shí)隊(duì)列服務(wù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02Golang中interface{}轉(zhuǎn)為數(shù)組的操作
這篇文章主要介紹了Golang中interface{}轉(zhuǎn)為數(shù)組的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04VScode下配置Go語言開發(fā)環(huán)境(2023最新)
在VSCode中配置Golang開發(fā)環(huán)境是非常簡單的,本文主要記錄了Go的安裝,以及給vscode配置Go的環(huán)境,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10Go內(nèi)存節(jié)省技巧簡單實(shí)現(xiàn)方法
這篇文章主要為大家介紹了Go內(nèi)存節(jié)省技巧簡單實(shí)現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01