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

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

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


访问者模式最重要的特性之一就是双重分发(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();
    }
}


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


好了,今天的分享到此结束。

相关文章
|
5天前
|
设计模式 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 设计模式与实战应用
【4月更文挑战第30天】本文探讨JavaScript设计模式在提升开发效率和代码质量中的关键作用。涵盖单例、工厂、观察者、装饰器和策略模式,并通过实例阐述其在全局状态管理、复杂对象创建、实时数据更新、功能扩展和算法切换的应用。理解并运用这些模式能帮助开发者应对复杂项目,提升前端开发能力。
|
3天前
|
设计模式 XML Java
第五篇 设计模式的选择和应用 - 智慧选择与合理实践
第五篇 设计模式的选择和应用 - 智慧选择与合理实践
|
3天前
|
Android开发
Android高级开发面试题以及笞案整理,实战解析
Android高级开发面试题以及笞案整理,实战解析
|
3天前
|
缓存 算法 前端开发
前端开发者必知的缓存淘汰策略:LRU算法解析与实践
前端开发者必知的缓存淘汰策略:LRU算法解析与实践
|
5天前
|
设计模式 Java 数据库连接
【企业场景】设计模式重点解析(下)
【企业场景】设计模式重点解析
16 0
|
5天前
|
设计模式 算法 Java
【企业场景】设计模式重点解析(上)
【企业场景】设计模式重点解析
11 0
|
5天前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
9 2
|
5天前
|
设计模式 安全 网络协议
【设计模式】代理模式例子解析
【设计模式】代理模式例子解析
10 2
|
5天前
|
设计模式 JavaScript 算法
js设计模式-策略模式与代理模式的应用
策略模式和代理模式是JavaScript常用设计模式。策略模式通过封装一系列算法,使它们可互换,让算法独立于客户端,提供灵活的选择。例如,定义不同计算策略并用Context类执行。代理模式则为对象提供代理以控制访问,常用于延迟加载或权限控制。如创建RealSubject和Proxy类,Proxy在调用RealSubject方法前可执行额外操作。这两种模式在复杂业务逻辑中发挥重要作用,根据需求选择合适模式解决问题。
|
5天前
|
监控 Linux 数据处理

热门文章

最新文章

推荐镜像

更多