掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流
大家好,這里是每周都在陪你一起進步的網(wǎng)管~!今天繼續(xù)學習設計模式—訪客模式

訪客模式也叫訪問者模式(Visitor Pattern)是一種將數(shù)據(jù)結構對象與數(shù)據(jù)操作分離的設計模式,可以在不改變數(shù)據(jù)結構對象類結構的前提下定義作用于這些對象的新的操作, 屬于行為型設計模式。
訪問者模式主要適用于以下應用場景:
訪問者模式通過將算法與對象結構分離來工作,這里說的算法指的是對對象的操作。為此,我們需要定義了一個表示算法的接口--Visitor。該接口將為對象結構中的每個類(一般稱為元素類)提供一個方法。每個方法都將元素類的一個實例作為參數(shù)。表示對象結構的所有元素類也會實現(xiàn)一個Element接口,該接口定義了接受訪問者的方法Accpet。此方法將訪問者接口的實現(xiàn)作為參數(shù)。當Accpet方法被調用時,訪問者實例對應的方法就會被調用,通過訪問者完成對元素類實例的操作。
下面我們看一下訪問者模式的類結構。
訪問者的類結構可以用下面的UML類圖來表示:
在這個用訪客模式實現(xiàn)不同維度的訂單統(tǒng)計的例子里,假設我們建設了一個訂單管理系統(tǒng), 現(xiàn)在系統(tǒng)中要求能按照不同維度統(tǒng)計分析銷售訂單
以后還有可能增加其他維度的銷售統(tǒng)計報表,針對這個需求我們可以根據(jù)訪問者模式, 可將不同的報表, 設計為訂單的訪問者。 首先定義訂單實體和它要實現(xiàn)的Element接口
"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設計模式】即可領取"
// 訂單服務接口
type IOrderService interface {
Save(order *Order) error
// 有的教程里把接收 visitor 實現(xiàn)的方法名定義成 Accept
Accept(visitor IOrderVisitor)
}
// 訂單實體類,實現(xiàn)IOrderService 接口
type Order struct {
ID int
Customer string
City string
Product string
Quantity int
}
func (mo *OrderService) Save(o *Order) error {
mo.orders[o.ID] = o
return nil
}
func (mo *OrderService) Accept(visitor IOrderVisitor) {
for _, v := range mo.orders {
visitor.Visit(v)
}
}
func NewOrder(id int, customer string, city string, product string, quantity int) *Order {
return &Order{
id, customer,city,product,quantity,
}
}
接下來定義生成各種銷售報表的訪客類,以及它們實現(xiàn)的訪客接口
"本文使用的完整可運行源碼
去公眾號「網(wǎng)管叨bi叨」發(fā)送【設計模式】即可領取"
type IOrderVisitor interface {
// 這里參數(shù)不能定義成 IOrderService
Visit(order *Order)
Report()
}
type CityVisitor struct {
cities map[string]int
}
func (cv *CityVisitor) Visit(o *Order) {
n, ok := cv.cities[o.City]
if ok {
cv.cities[o.City] = n + o.Quantity
} else {
cv.cities[o.City] = o.Quantity
}
}
func (cv *CityVisitor) Report() {
for k,v := range cv.cities {
fmt.Printf("city=%s, sum=%v\n", k, v)
}
}
func NewCityVisitor() IOrderVisitor {
return &CityVisitor{
cities: make(map[string]int, 0),
}
}
// 品類銷售報表, 按產(chǎn)品匯總銷售情況, 實現(xiàn)ISaleOrderVisitor接口
type ProductVisitor struct {
products map[string]int
}
func (pv *ProductVisitor) Visit(it *Order) {
n,ok := pv.products[it.Product]
if ok {
pv.products[it.Product] = n + it.Quantity
} else {
pv.products[it.Product] = it.Quantity
}
}
func (pv *ProductVisitor) Report() {
for k,v := range pv.products {
fmt.Printf("product=%s, sum=%v\n", k, v)
}
}
func NewProductVisitor() IOrderVisitor {
return &ProductVisitor{
products: make(map[string]int,0),
}
}
最后我們嘗試使用Vistor生成各種銷售報表
func main() {
orderService := NewOrderService()
orderService.Save(NewOrder(1, "張三", "廣州", "電視", 10))
orderService.Save(NewOrder(2, "李四", "深圳", "冰箱", 20))
orderService.Save(NewOrder(3, "王五", "東莞", "空調", 30))
orderService.Save(NewOrder(4, "張三三", "廣州", "空調", 10))
orderService.Save(NewOrder(5, "李四四", "深圳", "電視", 20))
orderService.Save(NewOrder(6, "王五五", "東莞", "冰箱", 30))
cv := NewCityVisitor()
orderService.Accept(cv)
cv.Report()
pv := NewProductVisitor()
orderService.Accept(pv)
pv.Report()
} 
我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網(wǎng)交流