设为首页 加入收藏

TOP

实践GoF的设计模式:访问者模式(二)
2023-07-23 13:28:59 】 浏览:61
Tags:实践 GoF 计模式
ok { return nil, ErrRecordNotFound } for _, r := range table.records { if reflect.DeepEqual(r.values[idx], value) { result = append(result, r) } } if len(result) == 0 { return nil, ErrRecordNotFound } return result, nil } } // 关键点3: 为Element定义Accept方法,入参为Visitor函数类型 func (t *Table) AcceptFunc(visitorFunc TableVisitorFunc) ([]interface{}, error) { return visitorFunc(t) }

客户端可以这么使用:

func client() {
 table := NewTable("testRegion").WithType(reflect.TypeOf(new(testRegion)))
 table.Insert(1, &testRegion{Id: 1, Name: "beijing"})
 table.Insert(2, &testRegion{Id: 2, Name: "beijing"})
 table.Insert(3, &testRegion{Id: 3, Name: "guangdong"})
    result, err := table.AcceptFunc(NewFieldEqVisitorFunc("name", "beijing"))
 if err != nil {
 t.Error(err)
 }
 if len(result) != 2 {
 t.Errorf("visit failed, want 2, got %d", len(result))
 }
}

Go 风格的实现,利用了函数闭包的特点,更加简洁了。

总结几个实现关键点:

  1. 定义一个访问者函数类型,函数签名以 Element 作为入参,上述例子为 TableVisitorFunc 类型。
  2. 定义一个工厂方法,工厂方法返回的是具体的访问访问者函数,上述例子为 NewFieldEqVisitorFunc 方法。这里利用了函数闭包的特性,在访问者函数中直接引用工厂方法的入参,与 FieldEqVisitor 中持有两个成员属性的效果一样。
  3. 为 Element 定义 Accept 方法,入参为 Visitor 函数类型 ,上述例子是 Table.AcceptFunc(...) 方法。

与迭代器模式结合

访问者模式经常与迭代器模式一起使用。比如上述例子中,如果你定义的 Visitor 实现不在 db 包内,那么就无法直接访问 Table 的数据,这时就需要通过 Table 提供的迭代器来实现。

在 简单的分布式应用系统(示例代码工程)中,db 模块存储的服务注册信息如下:

// demo/service/registry/model/service_profile.go
package model
// ServiceProfileRecord 存储在数据库里的类型
type ServiceProfileRecord struct {
    Id       string // 服务ID
    Type     ServiceType // 服务类型
    Status   ServiceStatus // 服务状态
    Ip       string // 服务IP
    Port     int // 服务端口
 RegionId string // 服务所属regionId
    Priority int // 服务优先级,范围0~100,值越低,优先级越高
    Load     int // 服务负载,负载越高表示服务处理的业务压力越大
}

现在,我们要查询符合指定 ServiceId 和 ServiceType 的服务记录,可以这么实现一个 Visitor:

// demo/service/registry/model/service_profile.go
package model
type ServiceProfileVisitor struct {
 svcId string
 svcType ServiceType
}
func (s *ServiceProfileVisitor) Visit(table *db.Table) ([]interface{}, error) {
 var result []interface{}
 // 通过迭代器来遍历Table的所有数据
 iter := table.Iterator()
 for iter.HasNext() {
 profile := new(ServiceProfileRecord)
 if err := iter.Next(profile); err != nil {
 return nil, err
 }
 // 先匹配ServiceId,如果一致则无须匹配ServiceType
 if profile.Id != "" && profile.Id == s.svcId {
            result = append(result, profile)
 continue
 }
 // ServiceId匹配不上,再匹配ServiceType
 if profile.Type != "" && profile.Type == s.svcType {
            result = append(result, profile)
 }
 }
 return result, nil
}

典型应用场景

  • k8s 中,kubectl 通过访问者模式来处理用户定义的各类资源。
  • 编译器中,通常使用访问者模式来实现对语法树解析,比如 LLVM。
  • 希望对一个复杂的数据结构执行某些操作,并支持后续扩展。

优缺点

优点

  • 数据结构和操作算法解耦,符合单一职责原则。
  • 支持对数据结构扩展多种操作,具备较强的可扩展性,符合开闭原则。

缺点

  • 访问者模式某种程度上,要求数据结构必须对外暴露其内在实现,否则访问者就无法遍历其中数据(可以结合迭代器模式来解决该问题)。
  • 如果被访问对象内的数据结构变更,可能要更新所有的访问者实现。

与其他模式的关联

  • 访问者模式 经常和迭代器模式一起使用,使得被访问对象无须向外暴露内在数据结构。
  • 也经常和组合模式一起使用,比如在语法树解析中,递归访问和解析树的每个节点(节点组合成树)。

文章配图

可以在 用Keynote画出手绘风格的配图 中找到文章的绘图方法。

参考

[1] 【Go实现】实践GoF的23种设计模式:SOLID原则, 元闰子

[2] 【Go实现】实践GoF的23种设计模式:迭代器模式, 元闰子

[3] Design Patterns, Chapter 5. Behavioral Patterns, GoF

[4] GO 编程模式:K8S VISITOR 模式, 酷壳

[5] 访问者模式refactoringguru.cn

 

点击关注,第一时间了解华为云新鲜技术~

首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇go-zero docker-compose 搭建课件.. 下一篇Go语言入门13(runtime包)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目