行为型:十. 访问者模式
访问者模式是什么
访问者是一种行为型设计模式, 允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为。
为什么用访问者模式
访问者模式建议将新行为放入一个名为访问者的独立类中,而不是试图将其整合到已有类中。现在需要执行操作的原始对象将作为参数被传递给访问者中的方法, 让方法能访问对象所包含的一切必要数据。当某个行为仅在类层次结构中的一些类中有意义,而在其他类中没有意义时, 可使用该模式。
访问者模式怎么实现
这里使用三个描述形状的结构体举例(circle,square,rectangle)。而计算它们的中心点和面积的方法采用了访问者的方式。这里的accept方法是改变了原有的封装。但这样的改变只会有一次,如果加入多个行为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("计算正方形的面积")
}
func (a *areaCalculator) visitForCircle(s *circle) {
a.area = int(math.Pi * float64(s.radius * s.radius))
fmt.Println("计算圆的面积")
}
func (a *areaCalculator) visitForRectangle(s *rectangle) {
a.area = s.l * s.b
fmt.Println("计算矩形的面积")
}
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("计算正方形的中心坐标")
}
func (a *middleCoordinates) visitForCircle(c *circle) {
a.x = c.radius
a.y = c.radius
fmt.Println("计算圆的中心坐标")
}
func (a *middleCoordinates) visitForRectangle(t *rectangle) {
a.x = t.b / 2
a.y = t.l / 2
fmt.Println("计算矩形的中心坐标")
}
example.go客户端调用示例
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)
}
//运行结果:
//计算正方形的面积
//计算圆的面积
//计算矩形的面积
//计算正方形的中心坐标
//计算圆的中心坐标
//计算矩形的中心坐标
优点
- 开闭原则。 你可以引入在不同类对象上执行的新行为, 且无需对这些类做出修改。
- 单一职责原则。 可将同一行为的不同版本移到同一个类中。
缺点
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。