访问者模式
访问者模式是一种行为设计模式, 它能将算法与其所作用的对象隔离开来。
模型说明
- 访问者(Visitor)接口声明了一系列以对象结构的具体元素为参数的访问者方法。如果编程语言支持重载,这些方法的名称可以是相同的,但是其参数一定是不同的。
- 具体访问者(Concrete Visitor)会为不同的具体元素类实现相同行为的几个不同版本。
- 元素(Element)接口声明了一个方法来“接收”访问者。该方法必须有一个参数被声明为访问者接口类型。
- 具体元素(Concrete Element)必须实现接收方法。该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。请注意,即使元素基类实现了该方法,所有子类都必须对其进行重写并调用访问者对象中的合适方法。
- 客户端(Client)通常会作为集合或其他复杂对象(例如一个组合树)的代表。客户端通常不知晓所有的具体元素类,因为它们会通过抽象接口与集合中的对象进行交互。
优缺点
1.优点
- *开闭原则:*你可以引入在不同类对象上执行的新行为,且无需对这些类做出修改。
- *单一职责原则:*可将同一行为的不同版本移到同一个类中。
- 访问者对象可以在与各种对象交互时收集一些有用的信息。当你想要遍历一些复杂的对象结构(例如对象树),并在结构中的每个对象上应用访问者时,这些信息可能会有所帮助。
2.缺点
- 每次在元素层次结构中添加或移除一个类时,你都要更新所有的访问者。
- 在访问者同某个元素进行交互时,它们可能没有访问元素私有成员变量和方法的必要权限。
使用场景
如果你需要对一个复杂对象结构(例如对象树)中的所有元素执行某些操作,可使用访问者模式。
可使用访问者模式来清理辅助行为的业务逻辑。
当某个行为仅在类层次结构中的一些类中有意义,而在其他类中没有意义时,可使用该模式。
参考代码
shape.go: 元件
package main
type Shape interface {
getType() string
accept(Visitor)
}
square.go: 具体元件
package main
type Square struct {
side int
}
func (s *Square) accept(v Visitor) {
v.visitForSquare(s)
}
func (s *Square) getType() string {
return "Square"
}
circle.go: 具体元件
package main
type Circle struct {
radius int
}
func (c *Circle) accept(v Visitor) {
v.visitForCircle(c)
}
func (c *Circle) getType() string {
return "Circle"
}
rectangle.go: 具体元件
package main
type Rectangle struct {
l int
b int
}
func (t *Rectangle) accept(v Visitor) {
v.visitForrectangle(t)
}
func (t *Rectangle) getType() string {
return "rectangle"
}
visitor.go: 访问者
package main
type Visitor interface {
visitForSquare(*Square)
visitForCircle(*Circle)
visitForRectangle(*Rectangle)
}
areaCalculator.go: 具体访问者
package main
import (
"fmt"
)
type AreaCalculator struct {
area int
}
func (a *AreaCalculator) visitForSquare(s *Square) {
// Calculate area for square.
// Then assign in to the area instance variable.
fmt.Println("Calculating area for square")
}
func (a *AreaCalculator) visitForCircle(s *Circle) {
fmt.Println("Calculating area for circle")
}
func (a *AreaCalculator) visitForRectangle(s *Rectangle) {
fmt.Println("Calculating area for rectangle")
}
middleCoordinates.go: 具体访问者
package main
import "fmt"
type MiddleCoordinates struct {
x int
y int
}
func (a *MiddleCoordinates) visitForSquare(s *Square) {
// Calculate middle point coordinates for square.
// Then assign in to the x and y instance variable.
fmt.Println("Calculating middle point coordinates for square")
}
func (a *MiddleCoordinates) visitForCircle(c *Circle) {
fmt.Println("Calculating middle point coordinates for circle")
}
func (a *MiddleCoordinates) visitForRectangle(t *Rectangle) {
fmt.Println("Calculating middle point coordinates for rectangle")
}
main.go: 客户端代码
package main
import "fmt"
func main() {
square := &Square{side: 2}
circle := &Circle{radius: 3}
rectangle := &Rectangle{l: 2, b: 3}
areaCalculator := &AreaCalculator{}
square.accept(areaCalculator)
circle.accept(areaCalculator)
rectangle.accept(areaCalculator)
fmt.Println()
middleCoordinates := &MiddleCoordinates{}
square.accept(middleCoordinates)
circle.accept(middleCoordinates)
rectangle.accept(middleCoordinates)
}
output:
Calculating area for square
Calculating area for circle
Calculating area for rectangle
Calculating middle point coordinates for square
Calculating middle point coordinates for circle
Calculating middle point coordinates for rectangle