What is the visitor pattern
Visitor is a behavioral design pattern that allows you to add new behaviors to the existing class hierarchy without modifying the existing code.
Why use visitor mode
Visitor pattern suggests putting new behavior into a separate class called visitor rather than trying to integrate it into existing classes. Now the original object that needs to perform the operation will be passed to the method in the visitor as a parameter, so that the method can access all the necessary data contained in the object. When there is no meaning in the hierarchy of some classes, it can be used only when there is no meaning in other classes.
How to implement visitor mode
Here, three examples of structures describing shapes are used (circle, square, rectangle). The method of calculating their center point and area adopts the way of visitors. The accept method here changes the original encapsulation. However, there will only be one such change. If multiple behaviors are added, only one can accept.
shape.go
package visitor
type shape interface {
getType() string
accept(visitor)
}
circle.go
package visitor
type circle struct {
radius int
}
func (c *circle) accept(v visitor) {
v.visitForCircle(c)
}
func (c *circle) getType() string {
return "Circle"
}
square.go
package visitor
type square struct {
side int
}
func (s *square) accept(v visitor) {
v.visitForSquare(s)
}
func (s *square) getType() string {
return "Square"
}
rectangle.go
package visitor
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 visitor
type visitor interface {
visitForSquare(*square)
visitForCircle(*circle)
visitForRectangle(*rectangle)
}
area_calculator.go
package visitor
import (
"fmt"
"math"
)
type areaCalculator struct {
area int
}
func (a *areaCalculator) visitForSquare(s *square) {
a.area = s.side * s.side
fmt. Println ("calculate square area")
}
func (a *areaCalculator) visitForCircle(s *circle) {
a.area = int(math.Pi * float64(s.radius * s.radius))
fmt. Println ("calculate area of circle")
}
func (a *areaCalculator) visitForRectangle(s *rectangle) {
a.area = s.l * s.b
fmt. Println ("calculate area of Rectangle")
}
middle_coordinates.go
package visitor
import "fmt"
type middleCoordinates struct {
x int
y int
}
func (a *middleCoordinates) visitForSquare(s *square) {
a.x = s.side / 2
a.y = s.side / 2
fmt. Println ("calculate the center coordinate of the square")
}
func (a *middleCoordinates) visitForCircle(c *circle) {
a.x = c.radius
a.y = c.radius
fmt. Println ("calculate the center coordinate of the circle")
}
func (a *middleCoordinates) visitForRectangle(t *rectangle) {
a.x = t.b / 2
a.y = t.l / 2
fmt. Println ("calculate the center coordinates of the rectangle")
}
example. Go client call example
package visitor
func Example() {
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)
middleCoordinates := &middleCoordinates{}
square.accept(middleCoordinates)
circle.accept(middleCoordinates)
rectangle.accept(middleCoordinates)
}
//Operation results:
//Calculate the area of a square
//Calculate the area of a circle
//Calculate the area of the rectangle
//Calculate the center coordinates of the square
//Calculate the center coordinates of the circle
//Calculate the center coordinates of the rectangle
advantage
- Opening and closing principle. You can introduce new behaviors that execute on different kinds of objects without modifying these classes.
- Single responsibility principle. Different versions of the same behavior can be moved to the same class.
shortcoming
- Adding new element classes is difficult. In the visitor mode, every time a new element class is added, the corresponding specific operation must be added to each specific visitor class, which violates the “opening and closing principle”.
- Destroy the package. In the visitor pattern, specific elements publish details to visitors, which destroys the encapsulation of objects.
- It violates the principle of inversion of dependence. The visitor pattern relies on concrete classes rather than abstract classes.