通过前文的介绍,我们已经对访问者模式有了一定的了解,并在简单示例中看到了它的基本应用。然而,访问者模式还有许多高级应用和实践技巧,让我们继续深入探索。
访问者模式最重要的特性之一就是双重分发(double dispatch)。在前面的示例中,我们通过元素的 accept
方法将访问者对象传递给元素,然后由元素调用访问者的 visit
方法。这种方式实现了根据元素的类型来决定调用哪个具体的访问者方法,从而实现了双重分发。
双重分发使得我们可以在运行时根据元素的类型和访问者的类型来决定执行的操作,而不是在编译时就确定。这种灵活性使得我们可以根据需要动态地添加新的元素类型和访问者类型,而不需要修改现有的代码。
当访问者模式与双重分发和其他设计模式结合使用时,可以实现更加灵活和强大的解决方案。让我们通过一个详细的案例代码来说明这些应用。
案例场景:
假设我们正在开发一个图形编辑器,其中包含多种图形元素,如圆形、矩形和三角形。我们希望能够对这些图形元素进行不同的操作,如绘制、移动、缩放等。同时,我们还希望能够实现撤销(Undo)和重做(Redo)的功能。为了实现这些需求,我们将访问者模式与双重分发和命令模式相结合使用。
- 双重分发的应用:
首先,我们定义访问者接口Visitor
和图形元素接口Shape
:
// 访问者接口 interface Visitor { void visit(Circle circle); void visit(Rectangle rectangle); void visit(Triangle triangle); } // 图形元素接口 interface Shape { void accept(Visitor visitor); }
然后,实现具体的图形元素类,分别是 Circle
、Rectangle
和Triangle
:
// 圆形类 class Circle implements Shape { private int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } @Override public void accept(Visitor visitor) { visitor.visit(this); } } // 矩形类 class Rectangle implements Shape { private int width; private int height; public Rectangle(int width, int height) { this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } @Override public void accept(Visitor visitor) { visitor.visit(this); } } // 三角形类 class Triangle implements Shape { private int base; private int height; public Triangle(int base, int height) { this.base = base; this.height = height; } public int getBase() { return base; } public int getHeight() { return height; } @Override public void accept(Visitor visitor) { visitor.visit(this); } }
接下来,我们实现具体的访问者类 DrawingVisitor
,用于绘制图形元素:
// 绘制访问者 class DrawingVisitor implements Visitor { @Override public void visit(Circle circle) { System.out.println("绘制圆形,半径:" + circle.getRadius()); } @Override public void visit(Rectangle rectangle) { System.out.println("绘制矩形,宽度:" + rectangle.getWidth() + ",高度:" + rectangle.getHeight()); } @Override public void visit(Triangle triangle) { System.out.println("绘制三角形,底边:" + triangle.getBase() + ",高度:" + triangle.getHeight()); } }
现在,我们可以创建图形元素并让绘制访问者对其进行操作:
public class Client { public static void main(String[] args) { Shape circle = new Circle(5); Shape rectangle = new Rectangle(10, 20); Shape triangle = new Triangle(8, 12); Visitor drawingVisitor = new DrawingVisitor(); circle.accept(drawingVisitor); rectangle.accept(drawingVisitor); triangle.accept(drawingVisitor); } }
输出结果:
绘制圆形,半径:5 绘制矩形,宽度:10,高度:20 绘制三角形,底边:8,高度:12
通过双重分发,我们可以根据具体的图形元素类型调用相应的访问者方法,实现了根据元素类型来决定执行的操作。
2、访问者模式与其他模式的结合:
在上述案例中,我们还将访问者模式与命令模式相结合,以实现撤销和重做功能。我们定义了两个命令接口 Command
和 UndoableCommand
,并实现了具体的命令类 DrawCommand
和 MoveCommand
:
// 命令接口 interface Command { void execute(); } // 可撤销的命令接口 interface UndoableCommand extends Command { void undo(); } // 绘制命令类 class DrawCommand implements UndoableCommand { private Shape shape; public DrawCommand(Shape shape) { this.shape = shape; } @Override public void execute() { shape.accept(new DrawingVisitor()); } @Override public void undo() { // 撤销绘制操作 } } // 移动命令类 class MoveCommand implements UndoableCommand { private Shape shape; private int deltaX; private int deltaY; public MoveCommand(Shape shape, int deltaX, int deltaY) { this.shape = shape; this.deltaX = deltaX; this.deltaY = deltaY; } @Override public void execute() { // 移动图形元素 } @Override public void undo() { // 撤销移动操作 } }
此外,我们还定义了一个命令历史记录类 CommandHistory
,用于管理命令的执行和撤销:
import java.util.Stack; // 命令历史记录类 class CommandHistory { private Stack<UndoableCommand> undoStack; public CommandHistory() { undoStack = new Stack<>(); } public void executeCommand(UndoableCommand command) { command.execute(); undoStack.push(command); } public void undo() { if (!undoStack.isEmpty()) { UndoableCommand command = undoStack.pop(); command.undo(); } } }
现在,我们可以通过命令模式来执行绘制和移动操作,并实现撤销和重做的功能:
public class Client { public static void main(String[] args) { Shape circle = new Circle(5); Shape rectangle = new Rectangle(10, 20); Shape triangle = new Triangle(8, 12); UndoableCommand drawCircleCommand = new DrawCommand(circle); UndoableCommand drawRectangleCommand = new DrawCommand(rectangle); UndoableCommand drawTriangleCommand = new DrawCommand(triangle); UndoableCommand moveRectangleCommand = new MoveCommand(rectangle, 5, 10); CommandHistory commandHistory = new CommandHistory(); // 执行绘制命令 commandHistory.executeCommand(drawCircleCommand); commandHistory.executeCommand(drawRectangleCommand); commandHistory.executeCommand(drawTriangleCommand); // 执行移动命令 commandHistory.executeCommand(moveRectangleCommand); // 撤销最后一个命令 commandHistory.undo(); } }
通过将访问者模式与双重分发和命令模式相结合,我们实现了对图形元素的绘制和移动操作,并且可以撤销和重做这些操作。这种组合使用的方式可以在复杂的应用场景中提供更大的灵活性和可扩展性,使代码结构更清晰、可维护性更高。
好了,今天的分享到此结束。