手撸golang 行为型设计模式 访问者模式

缘起

最近温习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采纳golang练习之

访问者模式

访问者模式(Visitor Pattern)是一种将数据结构与数据操作拆散的设计模式,指封装一些作用于某种数据结构中的各元素的操作,能够在不扭转数据结构的前提下定义作用于这些元素的新的操作,属于行为型设计模式。访问者模式次要实用于以下利用场景:(1)数据结构稳固,作用于数据结构的操作常常变动的场景。(2)须要数据结构与数据操作拆散的场景。(3)须要对不同数据类型(元素)进行操作,而不应用分支判断具体类型的场景。(摘自 谭勇德 <<设计模式就该这样学>>)

场景

  • 某订单管理系统, 须要按不同维度统计分析销售订单
  • 区域销售报表: 需按销售区域, 统计销售状况
  • 品类销售报表: 需依据不同产品, 统计销售状况
  • 依据访问者模式, 可将不同的报表, 设计为销售订单的访问者

设计

  • SaleOrder: 销售订单实体类
  • ISaleOrderService: 销售订单服务接口
  • ISaleOrderVisitor: 销售订单访问者
  • tMockSaleOrderService: 虚构的销售订单服务, 实现ISaleOrderService接口
  • CityVisitor: 区域销售报表, 按城市汇总销售状况, 实现ISaleOrderVisitor接口
  • ProductVisitor: 品类销售报表, 按产品汇总销售状况, 实现ISaleOrderVisitor接口

单元测试

visitor_pattern_test.go

package behavioral_patternsimport (    "learning/gooop/behavioral_patterns/visitor"    "testing")func Test_VisitorPattern(t *testing.T) {    // prepare sale orders    service := visitor.MockSaleOrderService    _ = service.Save(visitor.NewSaleOrder(1, "张三", "广州", "电视", 10))    _ = service.Save(visitor.NewSaleOrder(2, "李四", "深圳", "冰箱", 20))    _ = service.Save(visitor.NewSaleOrder(3, "王五", "东莞", "空调", 30))    _ = service.Save(visitor.NewSaleOrder(4, "张三三", "广州", "空调", 10))    _ = service.Save(visitor.NewSaleOrder(5, "李四四", "深圳", "电视", 20))    _ = service.Save(visitor.NewSaleOrder(6, "王五五", "东莞", "冰箱", 30))    // test CityVisitor    cv := visitor.NewCityVisitor()    service.Visit(cv)    cv.Report()    // test ProductVisitor    pv := visitor.NewProductVisitor()    service.Visit(pv)    pv.Report()}

测试输入

$ go test -v visitor_pattern_test.go === RUN   Test_VisitorPatterncity=东莞, sum=60city=广州, sum=20city=深圳, sum=40product=空调, sum=40product=电视, sum=30product=冰箱, sum=50--- PASS: Test_VisitorPattern (0.00s)PASSok      command-line-arguments  0.002s

SaleOrder.go

销售订单实体类

package visitortype SaleOrder struct {    ID int    Customer string    City string    Product string    Quantity int}func NewSaleOrder(id int, customer string, city string, product string, quantity int) *SaleOrder {    return &SaleOrder{        id, customer,city,product,quantity,    }}

ISaleOrderService.go

销售订单服务接口

package visitortype ISaleOrderService interface {    Save(order *SaleOrder) error    Visit(visitor ISaleOrderVisitor)}

ISaleOrderVisitor.go

销售订单访问者

package visitortype ISaleOrderVisitor interface {    Visit(it *SaleOrder)    Report()}

tMockSaleOrderService.go

虚构的销售订单服务, 实现ISaleOrderService接口

package visitortype tMockSaleOrderService struct {    orders map[int]*SaleOrder}func newMockSaleOrderService() ISaleOrderService {    return &tMockSaleOrderService{        orders: make(map[int]*SaleOrder, 0),    }}func (me *tMockSaleOrderService) Save(it *SaleOrder) error {    me.orders[it.ID] = it    return nil}func (me *tMockSaleOrderService) Visit(visitor ISaleOrderVisitor) {    for _,v := range me.orders {        visitor.Visit(v)    }}var MockSaleOrderService = newMockSaleOrderService()

CityVisitor.go

区域销售报表, 按城市汇总销售状况, 实现ISaleOrderVisitor接口

package visitorimport "fmt"type CityVisitor struct {    cities map[string]int}func NewCityVisitor() ISaleOrderVisitor {    return &CityVisitor{        cities: make(map[string]int,0),    }}func (me *CityVisitor) Visit(it *SaleOrder) {    n,ok := me.cities[it.City]    if ok {        me.cities[it.City] = n + it.Quantity    } else {        me.cities[it.City] = it.Quantity    }}func (me *CityVisitor) Report() {    for k,v := range me.cities {        fmt.Printf("city=%s, sum=%v\n", k, v)    }}

ProductVisitor.go

品类销售报表, 按产品汇总销售状况, 实现ISaleOrderVisitor接口

package visitorimport "fmt"type ProductVisitor struct {    products map[string]int}func NewProductVisitor() ISaleOrderVisitor {    return &ProductVisitor{        products: make(map[string]int,0),    }}func (me *ProductVisitor) Visit(it *SaleOrder) {    n,ok := me.products[it.Product]    if ok {        me.products[it.Product] = n + it.Quantity    } else {        me.products[it.Product] = it.Quantity    }}func (me *ProductVisitor) Report() {    for k,v := range me.products {        fmt.Printf("product=%s, sum=%v\n", k, v)    }}

访问者模式小结

访问者模式的长处(1)解耦了数据结构与数据操作,使得操作汇合能够独立变动。(2)能够通过扩大访问者角色,实现对数据集的不同操作,程序扩展性更好。(3)元素具体类型并非繁多,访问者均可操作。(4)各角色职责拆散,合乎繁多职责准则。访问者模式的毛病(1)无奈减少元素类型:若零碎数据结构对象易于变动,        常常有新的数据对象减少进来,    则访问者类必须减少对应元素类型的操作,违反了开闭准则。(2)具体元素变更艰难:具体元素减少属性、删除属性等操作,     会导致对应的访问者类须要进行相应的批改,    尤其当有大量访问者类时,批改范畴太大。(3)违反依赖倒置准则:为了达到“区别对待”,        访问者角色依赖的是具体元素类型,而不是形象。    (摘自 谭勇德 <<设计模式就该这样学>>)

(end)