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

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

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


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


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


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

相关文章
|
2月前
|
设计模式 人工智能 算法
基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。
|
3月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
6月前
|
设计模式 XML JSON
【设计模式】【行为型模式】访问者模式(Visitor)
一、入门 什么是访问者模式? 访问者模式(Visitor Pattern)是一种行为设计模式,允许你将算法与对象结构分离。通过这种方式,可以在不改变对象结构的情况下,向对象结构中的元素添加新的操作。
205 10
|
8月前
|
设计模式 SQL Java
【再谈设计模式】解释器模式~语法的解析执行者
解释器模式定义了一种语言的语法表示,并定义一个解释器来解释该语言中的句子。它使用类来表示每个语法规则,并且通过递归调用这些类的方法来解释表达式。本质上,它将一个复杂的表达式分解为一系列简单的部分,然后按照特定的语法规则进行解析和执行。
190 8
|
11月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
11月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
11月前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
11月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
11月前
|
设计模式 Java 程序员
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
本系列文章聚焦于面向对象软件设计中的设计模式,旨在帮助开发人员掌握23种经典设计模式及其应用。内容分为三大部分:第一部分介绍设计模式的概念、UML图和软件设计原则;第二部分详细讲解创建型、结构型和行为型模式,并配以代码示例;第三部分通过自定义Spring的IOC功能综合案例,展示如何将常用设计模式应用于实际项目中。通过学习这些内容,读者可以提升编程能力,提高代码的可维护性和复用性。
2244 1
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
|
9月前
|
设计模式 算法 Java
【再谈设计模式】访问者模式~操作对象结构的新视角
  访问者模式是一种行为设计模式,旨在解决对象结构与操作逻辑的耦合问题。在软件系统开发中,当面临复杂的对象结构(如多种类型对象组成的树形或图形结构),且需要对这些对象执行不同操作时,传统方式将操作直接写在对象类中会导致类职责过多,不利于维护和扩展。而访问者模式通过将操作与对象结构分离,允许在不改变现有对象结构的情况下定义新操作,元素接受访问者访问,访问者定义对不同类型元素的操作逻辑,从而为应对这种复杂情况提供了有效的解决方案。
137 0

推荐镜像

更多
  • DNS