二十三种设计模式全面解析-访问者模式的高级应用和实践技巧


通过前文的介绍,我们已经对访问者模式有了一定的了解,并在简单示例中看到了它的基本应用。然而,访问者模式还有许多高级应用和实践技巧,让我们继续深入探索。


访问者模式最重要的特性之一就是双重分发(double dispatch)。在前面的示例中,我们通过元素的 accept 方法将访问者对象传递给元素,然后由元素调用访问者的 visit 方法。这种方式实现了根据元素的类型来决定调用哪个具体的访问者方法,从而实现了双重分发。


双重分发使得我们可以在运行时根据元素的类型和访问者的类型来决定执行的操作,而不是在编译时就确定。这种灵活性使得我们可以根据需要动态地添加新的元素类型和访问者类型,而不需要修改现有的代码。


当访问者模式与双重分发和其他设计模式结合使用时,可以实现更加灵活和强大的解决方案。让我们通过一个详细的案例代码来说明这些应用。

案例场景:
假设我们正在开发一个图形编辑器,其中包含多种图形元素,如圆形、矩形和三角形。我们希望能够对这些图形元素进行不同的操作,如绘制、移动、缩放等。同时,我们还希望能够实现撤销(Undo)和重做(Redo)的功能。为了实现这些需求,我们将访问者模式与双重分发和命令模式相结合使用。


  1. 双重分发的应用:
    首先,我们定义访问者接口 Visitor 和图形元素接口 Shape
// 访问者接口
interface Visitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
    void visit(Triangle triangle);
}

// 图形元素接口
interface Shape {
    void accept(Visitor visitor);
}

然后,实现具体的图形元素类,分别是 CircleRectangleTriangle

// 圆形类
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、访问者模式与其他模式的结合:
在上述案例中,我们还将访问者模式与命令模式相结合,以实现撤销和重做功能。我们定义了两个命令接口 CommandUndoableCommand,并实现了具体的命令类 DrawCommandMoveCommand

// 命令接口
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();
    }
}

通过将访问者模式与双重分发和命令模式相结合,我们实现了对图形元素的绘制和移动操作,并且可以撤销和重做这些操作。这种组合使用的方式可以在复杂的应用场景中提供更大的灵活性和可扩展性,使代码结构更清晰、可维护性更高。


好了,今天的分享到此结束。如果觉得我的博文帮到了您,您的点赞和关注是对我最大的支持。如遇到什么问题,可评论区留言。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w风雨无阻w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值