鱼弦:全栈领域创作新星创作者 、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen)
在Go设计模式中,访问者模式(Visitor Pattern)是一种行为型模式,用于将算法与数据结构分离,使得算法可以独立地操作数据结构的不同元素,同时遵循开闭原则。
原理详细解释:
访问者模式的核心思想是将数据结构与对数据的操作分离。它通过定义一个访问者(Visitor)接口,该接口包含了对数据结构中每个元素的操作方法。数据结构中的每个元素都要实现一个接受访问者的方法,将自身传递给访问者,并在访问者中调用相应的操作方法。
在访问者模式中,通常有以下几个角色:
- Visitor(访问者接口):定义了对数据结构中每个元素的操作方法。
- ConcreteVisitor(具体访问者类):实现了Visitor接口,具体实现了对数据结构中每个元素的操作方法。
- Element(元素接口):定义了接受访问者的方法。
- ConcreteElement(具体元素类):实现了Element接口,实现了接受访问者的方法,并将自身传递给访问者。
- ObjectStructure(对象结构):包含了一个或多个元素,提供了遍历元素的方法。
访问者模式的优点在于可以在不修改数据结构的情况下,增加新的操作。但缺点是增加新的元素类型会较为困难,需要修改所有的访问者类。
底层结构图:
以下是访问者模式的底层结构图:
+-------------------------+
| Visitor |
+-------------------------+
| + VisitConcreteElementA |
| + VisitConcreteElementB |
+-------------------------+
/|\
|
|
+-------------------------+
| ConcreteVisitorA |
+-------------------------+
| + VisitConcreteElementA |
+-------------------------+
/|\
|
|
+-------------------------+
| ConcreteVisitorB |
+-------------------------+
| + VisitConcreteElementB |
+-------------------------+
/|\
|
|
+-----------------------+
| Element |
+-----------------------+
| + Accept(Visitor) |
+-----------------------+
/|\
|
|
+-----------------------+
| ConcreteElementA |
+-----------------------+
| + Accept(Visitor) |
+-----------------------+
/|\
|
|
+-----------------------+
| ConcreteElementB |
+-----------------------+
| + Accept(Visitor) |
+-----------------------+
|
|
+-----------------------+
| ObjectStructure |
+-----------------------+
| + Attach(Element) |
| + Detach(Element) |
| + Accept(Visitor) |
+-----------------------+
使用场景解释:
访问者模式适用于以下情况:
- 当一个数据结构中的元素类型很少发生变化,但需要定义多个不同的操作时,可以使用访问者模式。它将每个操作都封装在一个独立的访问者类中,使得操作可以独立地变化和扩展。
- 当一个数据结构中的元素类型较多,但每个元素都需要进行不同的操作时,可以使用访问者模式。通过访问者模式,可以避免在数据结构中添加大量的条件语句,使得代码更加清晰和可维护。
- 当对数据结构的元素进行操作时,不希望修改元素的类定义,可以使用访问者模式。访问者模式将操作封装在访问者类中,使得操作可以独立于元素类进行扩展。
代码示例实现:
下面是一个简单的访问者模式的示例,假设有以下是一个使用Golang实现的访问者模式示例:
package main
import "fmt"
// Visitor 访问者接口
type Visitor interface {
VisitConcreteElementA(element ConcreteElementA)
VisitConcreteElementB(element ConcreteElementB)
}
// ConcreteVisitorA 具体访问者A
type ConcreteVisitorA struct{}
// VisitConcreteElementA 具体访问者A对ConcreteElementA的操作
func (v ConcreteVisitorA) VisitConcreteElementA(element ConcreteElementA) {
fmt.Println("ConcreteVisitorA访问ConcreteElementA")
}
// VisitConcreteElementB 具体访问者A对ConcreteElementB的操作
func (v ConcreteVisitorA) VisitConcreteElementB(element ConcreteElementB) {
fmt.Println("ConcreteVisitorA访问ConcreteElementB")
}
// ConcreteVisitorB 具体访问者B
type ConcreteVisitorB struct{}
// VisitConcreteElementA 具体访问者B对ConcreteElementA的操作
func (v ConcreteVisitorB) VisitConcreteElementA(element ConcreteElementA) {
fmt.Println("ConcreteVisitorB访问ConcreteElementA")
}
// VisitConcreteElementB 具体访问者B对ConcreteElementB的操作
func (v ConcreteVisitorB) VisitConcreteElementB(element ConcreteElementB) {
fmt.Println("ConcreteVisitorB访问ConcreteElementB")
}
// Element 元素接口
type Element interface {
Accept(visitor Visitor)
}
// ConcreteElementA 具体元素A
type ConcreteElementA struct{}
// Accept 接受访问者的操作
func (e ConcreteElementA) Accept(visitor Visitor) {
visitor.VisitConcreteElementA(e)
}
// ConcreteElementB 具体元素B
type ConcreteElementB struct{}
// Accept 接受访问者的操作
func (e ConcreteElementB) Accept(visitor Visitor) {
visitor.VisitConcreteElementB(e)
}
// ObjectStructure 对象结构
type ObjectStructure struct {
elements []Element
}
// Attach 添加元素
func (o *ObjectStructure) Attach(element Element) {
o.elements = append(o.elements, element)
}
// Detach 移除元素
func (o *ObjectStructure) Detach(element Element) {
for i, e := range o.elements {
if e == element {
o.elements = append(o.elements[:i], o.elements[i+1:]...)
break
}
}
}
// Accept 接受访问者的操作
func (o ObjectStructure) Accept(visitor Visitor) {
for _, element := range o.elements {
element.Accept(visitor)
}
}
func main() {
// 创建具体元素
elementA := ConcreteElementA{}
elementB := ConcreteElementB{}
// 创建具体访问者
visitorA := ConcreteVisitorA{}
visitorB := ConcreteVisitorB{}
// 创建对象结构
objectStructure := ObjectStructure{}
objectStructure.Attach(elementA)
objectStructure.Attach(elementB)
// 对象结构接受具体访问者A的操作
objectStructure.Accept(visitorA)
// 对象结构接受具体访问者B的操作
objectStructure.Accept(visitorB)
}
在上面的示例中,我们定义了Visitor接口和两个具体的Visitor类:ConcreteVisitorA和ConcreteVisitorB。然后我们定义了Element接口和两个具体的Element类:ConcreteElementA和ConcreteElementB。最后,我们定义了ObjectStructure类来管理元素,并实现了Attach、Detach和Accept方法。在main函数中,我们创建了具体的元素和访问者,并将元素添加到对象结构中。然后,我们分别让对象结构接受具体访问者A和具体访问者B的操作。
文献材料链接:
以下是一些关于访问者模式的参考文献链接:
- Design Patterns: Elements of Reusable Object-Oriented Software (Gang of Four book)
- 链接:Amazon.com
- SourceMaking - Visitor Pattern
- Refactoring Guru - Visitor Pattern
- 链接:Visitor
当前都有哪些产品在使用:
访问者模式是一种常用的设计模式,可以在许多不同的领域和产品中找到应用。以下是一些使用访问者模式的常见产品和框架:
- 编译器和解析器:访问者模式常用于编译器和解析器的实现中,用于遍历抽象语法树(Abstract Syntax Tree,AST)并执行相应的操作。
- 文档处理工具:访问者模式可以用于处理文档结构,例如解析和操作HTML、XML或Markdown文档。
- 图形处理库:访问者模式可以用于图形处理库,用于遍历图形对象的层次结构并执行相应的操作,如绘制、变换或计算。
- 数据库访问框架:访问者模式可以用于数据库访问框架,用于遍历数据库表或查询结果,并执行相应的操作,如映射到对象模型或执行业务逻辑。
- 游戏引擎:访问者模式可以用于游戏引擎中的场景管理、碰撞检测和物理引擎等方面,用于遍历游戏对象并执行相应的操作。