设为首页 加入收藏

TOP

实践GoF的设计模式:访问者模式(一)
2023-07-23 13:28:59 】 浏览:58
Tags:实践 GoF 计模式
摘要:访问者模式的目的是,解耦数据结构和算法,使得系统能够在不改变现有代码结构的基础上,为对象新增一种新的操作。

本文分享自华为云社区《【Go实现】实践GoF的23种设计模式:访问者模式》,作者:元闰子 。

简介

GoF 对访问者模式(Visitor Pattern)的定义如下:

Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

访问者模式的目的是,解耦数据结构和算法,使得系统能够在不改变现有代码结构的基础上,为对象新增一种新的操作。

上一篇介绍的迭代器模式也做到了数据结构和算法的解耦,不过它专注于遍历算法。访问者模式,则在遍历的同时,将操作作用到数据结构上,一个常见的应用场景是语法树的解析。

UML 结构

场景上下文

在 简单的分布式应用系统(示例代码工程)中,db 模块用来存储服务注册和监控信息,它是一个 key-value 数据库。另外,我们给 db 模块抽象出 Table 对象:

// demo/db/table.go
package db
// Table 数据表定义
type Table struct {
    name            string
    metadata        map[string]int // key为属性名,value属性值的索引, 对应到record上存储
    records         map[interface{}]record
 iteratorFactory TableIteratorFactory // 默认使用随机迭代器
}

目的是提供类似于关系型数据库的按列查询能力,比如:

上述的按列查询只是等值比较,未来还可能会实现正则表达式匹配等方式,因此我们需要设计出可供未来扩展的接口。这种场景,使用访问者模式正合适。

代码实现

// demo/db/table_visitor.go
package db
// 关键点1: 定义表查询的访问者抽象接口,允许后续扩展查询方式
type TableVisitor interface {
 // 关键点2: Visit方法以Element作为入参,这里的Element为Table对象
 Visit(table *Table) ([]interface{}, error)
}
// 关键点3: 定义Visitor抽象接口的实现对象,这里FieldEqVisitor实现按列等值查询逻辑
type FieldEqVisitor struct {
    field string
    value interface{}
}
// 关键点4: 为FieldEqVisitor定义Visit方法,实现具体的等值查询逻辑
func (f *FieldEqVisitor) Visit(table *Table) ([]interface{}, error) {
 result := make([]interface{}, 0)
 idx, ok := table.metadata[f.field]
 if !ok {
 return nil, ErrRecordNotFound
 }
 for _, r := range table.records {
 if reflect.DeepEqual(r.values[idx], f.value) {
            result = append(result, r)
 }
 }
 if len(result) == 0 {
 return nil, ErrRecordNotFound
 }
 return result, nil
}
func NewFieldEqVisitor(field string, value interface{}) *FieldEqVisitor {
 return &FieldEqVisitor{
        field: field,
        value: value,
 }
}
// demo/db/table.go
package db
type Table struct {...}
// 关键点5: 为Element定义Accept方法,入参为Visitor接口
func (t *Table) Accept(visitor TableVisitor) ([]interface{}, error) {
 return visitor.Visit(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"})
 visitor := NewFieldEqVisitor("name", "beijing")
    result, err := table.Accept(visitor)
 if err != nil {
 t.Error(err)
 }
 if len(result) != 2 {
 t.Errorf("visit failed, want 2, got %d", len(result))
 }
}

总结实现访问者模式的几个关键点:

  1. 定义访问者抽象接口,上述例子为 TableVisitor, 目的是允许后续扩展表查询方式。
  2. 访问者抽象接口中,Visit 方法以 Element 作为入参,上述例子中, Element 为 Table 对象。
  3. 为 Visitor 抽象接口定义具体的实现对象,上述例子为 FieldEqVisitor。
  4. 在访问者的 Visit 方法中实现具体的业务逻辑,上述例子中 FieldEqVisitor.Visit(...) 实现了按列等值查询逻辑。
  5. 在被访问者 Element 中定义 Accept 方法,以访问者 Visitor 作为入参。上述例子中为 Table.Accept(...) 方法。

扩展

Go 风格实现

上述实现是典型的面向对象风格,下面以 Go 风格重新实现访问者模式:

// demo/db/table_visitor_func.go
package db
// 关键点1: 定义一个访问者函数类型
type TableVisitorFunc func(table *Table) ([]interface{}, error)
// 关键点2: 定义工厂方法,工厂方法返回的是一个访问者函数,实现了具体的访问逻辑
func NewFieldEqVisitorFunc(field string, value interface{}) TableVisitorFunc {
 return func(table *Table) ([]interface{}, error) {
 result := make([]interface{}, 0)
 idx, ok := table.metadata[field]
 if !
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇go-zero docker-compose 搭建课件.. 下一篇Go语言入门13(runtime包)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目