定义
访问者模式(Visitor Pattern)是一种行为型设计模式,它允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。访问者模式可以在不改变对象结构的情况下,为对象结构中的每个元素添加新的操作。
以下是访问者模式的主要特点和定义:
主要角色
-
Visitor(访问者) :
- 定义对每个元素的访问操作。
- 通常包含多个访问方法,每个方法对应一个具体的元素类型。
-
Element(元素) :
- 定义一个接受访问者的接口。
-
ConcreteElement(具体元素) :
- 实现元素接口,提供接受访问者的具体实现。
-
ObjectStructure(对象结构) :
- 可以是一个组合结构或者集合,用于存储元素对象,并提供遍历元素的方法,以便访问者可以访问每个元素。
业务
假设我们有一个图形绘制系统,其中有不同类型的图形(圆形、矩形、三角形),我们希望能够对这些图形进行不同的操作,如计算面积和绘制图形。我们可以使用访问者模式来实现这个功能。
classDiagram
class Shape {
+accept(Visitor) : void
}
class Circle {
-double radius
+Circle(double)
+getRadius() : double
+accept(Visitor) : void
}
class Rectangle {
-double width
-double height
+Rectangle(double, double)
+getWidth() : double
+getHeight() : double
+accept(Visitor) : void
}
class Triangle {
-double side1
-double side2
-double side3
+Triangle(double, double, double)
+getSide1() : double
+getSide2() : double
+getSide3() : double
+accept(Visitor) : void
}
class Visitor {
+visit(Circle) : void
+visit(Rectangle) : void
+visit(Triangle) : void
}
class AreaVisitor {
+visit(Circle) : void
+visit(Rectangle) : void
+visit(Triangle) : void
}
class DrawVisitor {
+visit(Circle) : void
+visit(Rectangle) : void
+visit(Triangle) : void
}
Shape <|-- Circle
Shape <|-- Rectangle
Shape <|-- Triangle
Visitor <|-- AreaVisitor
Visitor <|-- DrawVisitor
一、定义元素接口(Element)
interface Shape {
void accept(Visitor visitor);
}
二、定义具体元素类(ConcreteElement)
- 圆形:
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public double getRadius() {
return radius;
}
}
- 矩形:
class Rectangle implements Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
}
- 三角形:
class Triangle implements Shape {
private double side1;
private double side2;
private double side3;
public Triangle(double side1, double side2, double side3) {
this.side1 = side1;
this.side2 = side2;
this.side3 = side3;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public double getSide1() {
return side1;
}
public double getSide2() {
return side2;
}
public double getSide3() {
return side3;
}
}
三、定义访问者接口(Visitor)
interface Visitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
void visit(Triangle triangle);
}
四、定义具体访问者类(ConcreteVisitor)
- 计算面积的访问者:
class AreaVisitor implements Visitor {
@Override
public void visit(Circle circle) {
double area = Math.PI * circle.getRadius() * circle.getRadius();
System.out.println("Area of circle: " + area);
}
@Override
public void visit(Rectangle rectangle) {
double area = rectangle.getWidth() * rectangle.getHeight();
System.out.println("Area of rectangle: " + area);
}
@Override
public void visit(Triangle triangle) {
double s = (triangle.getSide1() + triangle.getSide2() + triangle.getSide3) / 2;
double area = Math.sqrt(s * (s - triangle.getSide1()) * (s - triangle.getSide2()) * (s - triangle.getSide3));
System.out.println("Area of triangle: " + area);
}
}
- 绘制图形的访问者:
class DrawVisitor implements Visitor {
@Override
public void visit(Circle circle) {
System.out.println("Drawing circle with radius " + circle.getRadius());
}
@Override
public void visit(Rectangle rectangle) {
System.out.println("Drawing rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight());
}
@Override
public void visit(Triangle triangle) {
System.out.println("Drawing triangle with sides " + triangle.getSide1() + ", " + triangle.getSide2() + ", " + triangle.getSide3());
}
}
五、使用访问者模式
public class VisitorPatternExample {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
Shape triangle = new Triangle(3, 4, 5);
Visitor areaVisitor = new AreaVisitor();
Visitor drawVisitor = new DrawVisitor();
circle.accept(areaVisitor);
circle.accept(drawVisitor);
rectangle.accept(areaVisitor);
rectangle.accept(drawVisitor);
triangle.accept(areaVisitor);
triangle.accept(drawVisitor);
}
}
在这个例子中,Shape
是元素接口,Circle
、Rectangle
和Triangle
是具体元素类。Visitor
是访问者接口,AreaVisitor
和DrawVisitor
是具体访问者类。每个具体元素类实现了accept
方法,该方法接受一个访问者对象,并调用访问者对象的相应方法。每个具体访问者类实现了访问者接口中的方法,对不同类型的元素进行特定的操作。通过这种方式,我们可以在不改变元素类的情况下,为元素类添加新的操作,实现了访问者模式的设计。
总结
使用场景
-
当一个对象结构包含很多不同类型的对象,并且需要对这些对象执行一些依赖于对象具体类型的操作时。
- 例如,一个图形编辑软件中,有不同类型的图形元素(圆形、矩形、三角形等),需要对这些图形元素进行不同的操作,如绘制、计算面积等。可以使用访问者模式,为每种操作定义一个访问者,然后让图形元素接受这些访问者的访问。
-
当需要对一个对象结构进行一些复杂的操作,而这些操作不适合放在对象结构的类中时。
-
访问者模式可以将这些复杂的操作封装在访问者类中,使得对象结构的类更加简洁。
-
优点
-
增加新的操作很容易。
- 只需要添加一个新的访问者类,而不需要修改对象结构的类。
-
分离了对象结构和操作。
- 使得操作的添加和修改更加灵活,不会影响到对象结构的代码。
缺点
-
增加新的元素类型比较困难。
- 需要修改每个访问者类,添加对新元素类型的处理方法。
-
破坏了对象的封装性。
- 访问者需要访问对象的内部状态,这可能会破坏对象的封装性。