Go设计模式--访客模式

访客模式也叫访问者模式(Visitor Pattern)是一种将数据结构对象与数据操作分离的设计模式,可以在不改变数据结构对象类结构的前提下定义作用于这些对象的新的操作, 属于行为型设计模式。

访客模式也叫访问者模式(Visitor Pattern)是一种将数据结构对象与数据操作分离的设计模式,可以在不改变数据结构对象类结构的前提下定义作用于这些对象的新的操作, 属于行为型设计模式。

大家好,这里是每周都在陪你一起进步的网管~!今天继续学习设计模式—访客模式

访客模式也叫访问者模式(Visitor Pattern)是一种将数据结构对象与数据操作分离的设计模式,可以在不改变数据结构对象类结构的前提下定义作用于这些对象的新的操作, 属于行为型设计模式。

访问者模式主要适用于以下应用场景:

  1. 数据结构稳定,作用于数据结构的操作经常变化的场景。
  2. 需要数据结构与数据操作分离的场景。
  3. 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景。

访客模式怎么工作?

访问者模式通过将算法与对象结构分离来工作,这里说的算法指的是对对象的操作。为此,我们需要定义了一个表示算法的接口--Visitor。该接口将为对象结构中的每个类(一般称为元素类)提供一个方法。每个方法都将元素类的一个实例作为参数。表示对象结构的所有元素类也会实现一个Element接口,该接口定义了接受访问者的方法Accpet。此方法将访问者接口的实现作为参数。当Accpet方法被调用时,访问者实例对应的方法就会被调用,通过访问者完成对元素类实例的操作。

下面我们看一下访问者模式的类结构。

访客模式结构

访问者的类结构可以用下面的UML类图来表示:

图片

  • 访客接口 (Visitor) 声明了一系列以表示对象结构的具体元素为参数的访问者方法。 如果编程语言支持重载, 这些方法的名称可以是相同的, 但是其参数一定是不同的。
  • 具体访客 (Concrete Visitor) 会为不同的具体元素类实现相同行为的几个不同版本。
  • 元素 (Element) 接口,声明了一个方法来 “接收” 访问者。 该方法必须有一个参数被声明为访问者接口类型。
  • 具体元素 (Concrete Element) 必须实现接收方法。 该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。 请注意, 即使元素基类实现了该方法, 所有子类都必须对其进行重写并调用访客对象中的合适方法。

访客模式代码示例

在这个用访客模式实现不同维度的订单统计的例子里,假设我们建设了一个订单管理系统, 现在系统中要求能按照不同维度统计分析销售订单

  • 区域销售报表: 需按销售区域, 统计销售情况
  • 品类销售报表: 需根据不同产品, 统计销售情况

以后还有可能增加其他维度的销售统计报表,针对这个需求我们可以根据访问者模式, 可将不同的报表, 设计为订单的访问者。 首先定义订单实体和它要实现的Element接口

"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
// 订单服务接口
type IOrderService interface {
Save(order *Order) error
// 有的教程里把接收 visitor 实现的方法名定义成 Accept
Accept(visitor IOrderVisitor)
}


// 订单实体类,实现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,
}
}

接下来定义生成各种销售报表的访客类,以及它们实现的访客接口

"本文使用的完整可运行源码
去公众号「网管叨bi叨」发送【设计模式】即可领取"
type IOrderVisitor interface {
// 这里参数不能定义成 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),
}
}

// 品类销售报表, 按产品汇总销售情况, 实现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()
}

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月7日 20:29
下一篇 2023年5月7日 20:29

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信