植物百科网
当前位置: 首页 农业百科

golang设计模式(实践GoF的设计模式)

时间:2023-08-04 作者: 小编 阅读量: 1 栏目名: 农业百科

简介GoF对访问者模式的定义如下:表示要对对象结构的元素执行的操作。访问者模式的目的是,解耦数据结构和算法,使得系统能够在不改变现有代码结构的基础上,为对象新增一种新的操作。这种场景,使用访问者模式正合适。为Visitor抽象接口定义具体的实现对象,上述例子为FieldEqVisitor。在访问者的Visit方法中实现具体的业务逻辑,上述例子中FieldEqVisitor.Visit(...)实现了按列等值查询逻辑。在被访问者Element中定义Accept方法,以访问者Visitor作为入参。

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

简介

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

表示要对对象结构的元素执行的操作。“访问器”允许您定义新操作,而无需更改操作该操作的元素的类。

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

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

UML 结构场景上下文

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

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

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

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

代码实现

// demo/db/table_visitor.gopackage db// 关键点1: 定义表查询的访问者抽象接口,允许后续扩展查询方式type TableVisitor interface {// 关键点2: Visit方法以Element作为入参,这里的Element为Table对象Visit(table *Table) ([]interface{}, error)}// 关键点3: 定义Visitor抽象接口的实现对象,这里FieldEqVisitor实现按列等值查询逻辑type FieldEqVisitor struct {field stringvalue 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.gopackage dbtype 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.gopackage 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 !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.gopackage model// ServiceProfileRecord 存储在数据库里的类型type ServiceProfileRecord struct {Id string // 服务IDType ServiceType // 服务类型Status ServiceStatus // 服务状态Ip string // 服务IPPort int // 服务端口RegionId string // 服务所属regionIdPriority int // 服务优先级,范围0~100,值越低,优先级越高Load int // 服务负载,负载越高表示服务处理的业务压力越大}

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

// demo/service/registry/model/service_profile.gopackage modeltype ServiceProfileVisitor struct {svcId stringsvcType 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,如果一致则无须匹配ServiceTypeif profile.Id != "" && profile.Id == s.svcId {result = append(result, profile)continue}// ServiceId匹配不上,再匹配ServiceTypeif 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] 设计模式,第5章。行为模式,去甲方

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

[5] 访问者模式, refactoringguru.cn

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

华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云

    推荐阅读
  • 突触名词解释(突触是什么意思)

    突触名词解释突触是指一个神经元的冲动传到另一个神经元或传到另一细胞间的相互接触的结构。突触是神经元之间在功能上发生联系的部位,也是信息传递的关键部位。在光学显微镜下,可以看到一个神经元的轴突末梢经过多次分支,最后每一小支的末端膨大呈杯状或球状,叫做突触小体。这些突触小体可以与多个神经元的细胞体或树突相接触,形成突触。从电子显微镜下观察,可以看到,这种突触是由突触前膜、突触间隙和突触后膜三部分构成。

  • 《守望先锋》对战局影响大招top一览 守望先锋对局战绩

    今天小编要为大家带来的是玩家“黑呦酱”分享的《守望先锋》对战局影响大招top一览,感兴趣的玩家赶紧一起来看看吧!守望先锋大招分为四类,控制类,自身BUFF类,辅助类以及伤害类,由于伤害类大部分使用大招时,本体无法进行有效杀伤,且控制类及自身BUFF类需要其他技能的配合,so,此间因素也要加入考量。

  • 运动后喝黑咖啡还能燃脂吗 运动时喝黑咖啡会加快燃脂吗?

    2、运动过程中身体脂肪会加速燃烧,从而具有一定减肥作用;而黑咖啡热量比较小,加上其中含有大量的咖啡因以及维生素、纤维素物质,适量喝可以促进人体肠胃蠕动,加速脂肪代谢分解,对减肥具有促进作用。

  • 斯威汽车质量怎么样(斯威质量好不好)

    2018年6月起,斯威“品质特工队”以四大火炉的重庆作为起点,途径海南、吐鲁番、格尔木三地,历时近一年进行了数十万公里极限环境适应性试验。极端干燥高温环境下,常见车内温度往往会狂飙到60℃以上,而在斯威G01的车厢里,却始终能够保持清新凉爽的状态。一整套严酷考验下来,斯威G01的性能表现完全得以充分认证。这样一算,斯威G01差不多完成了近百万公里的专业级严酷考验。

  • 春天兰花怎么养 春天兰花怎么养浇水

    白墨兰花哪个品种最好白墨兰花是墨兰的珍贵变异品种假鳞茎椭圆形,已有数百年栽培历史,流传至今,不下十数个品种,它叶色莹润、体态优雅、幽香静远、且抗病,白墨兰花比较好的品种一般分企剑和软剑两个品系。什么兰花开花最香兰花品种很多,按花香来排,在兰花界春兰居首,惠兰次之,随后便是建兰、墨兰和寒兰,春兰的花香味最正宗,持久性也极强。

  • 奔驰e300l前进挡总共有几个(你看了奔驰22款E300L升级这套原厂HUD抬头显示效果觉得怎么样)

    从行车安全的角度来考虑,加装一台HUD是非常有必要的。HUD的全称是HeadUpDisplay,中文翻译过来就是抬头显示器。今天星骏汇小陈通过以上的产品配件图了解,我们看到这台奔驰22款E300L升级HUD抬头显示所需要更换的配件有,抬显仪器,高配仪表盖板,高配仪表电脑,雨量传感器,空调管升级HUD抬头显示把仪表台上的那一块盖板换掉,换成高配的预留好显示器孔位的盖板,装上显示器,从而使仪表显示的内容投射到挡风玻璃上面。

  • 儿童葫芦丝表演(通城千人共奏葫芦丝)

    儿童葫芦丝表演香城都市报讯 10月27日,通城县隽水中学参加湖北省“黄鹤杯”美育节节目视频录制现场,七、八年级千名学生,同奏乐曲《龙的传人》。该校相关负责人介绍,本学期,每天下午预备铃响5分钟,七、八年级各班集体合奏葫芦丝。丝竹声声,已渐成校园一道靓丽的风景线。近年来,该校贯彻落实社会主义核心价值观,注重未成年人思想道德建设,坚持开设中华传统和特色民族特色教育课程,促进学生“德智体”全面发展。

  • 鸡娃时代孩子的成长之道(与其1岁就开始鸡娃)

    出生时大脑发育已经完成25%,1岁完成了50%,3岁完成了60%,6岁达到90%。现在小学虽然是零基础入学,取消了统一考试,但是它对学生的要求并没有降低。吃够了佛系养娃的亏,橙子家的老二断然不肯再佛系养了。北京卫视于2018年摄制的纪录片《起跑线》中,有一个7岁的北京女孩令人印象深刻。她的家庭,在北京三环内有一套房,一辆车。妈妈认为,孩子从小培养兴趣,靠的是父母的指引。

  • 环氧树脂的作用与用途(环氧树脂有什么作用与用途)

    环氧树脂的作用与用途具有优良的物理和电绝缘性能,强度高、收缩性低,耐腐蚀以及有高绝缘的优势,所以被称为万能胶。电器、电机绝缘封装件的浇注。从常压浇注、真空浇注已发展到自动压力凝胶成型。长时间接触胶水时,有人会有细微的皮肤过敏和细微瘙痒疼痛的情况,建议在运用时戴上防护手套,如果出现了这样的情况,需要用酒精擦洗,然后用清水冲洗干净。

  • 明月曾照江东寒剧情(明月曾照江东寒剧情介绍)

    明月曾照江东寒剧情剧情简介:美少女战清泓是武林副盟主战破敌之女,从小被父亲禁止涉及江湖事。十年一期的武林大会即将来临,战清泓瞒着家人偷跑下山,立志夺取武林盟主之位。战清泓与温宥也开始互生情愫,奈何最终被世俗礼法所阻碍。与此同时,江湖上风起云涌,战清泓发现自己自幼背诵的家训竟是人人趋之若鹜的第一神功《鹤羽剑法》。