java设计模式六 访问者

访问者模式(Visitor Pattern)是一种设计模式,它允许你将算法附加到对象结构中的各个元素上,而不必修改对象结构本身。它主要用于处理对象结构非常稳定,但频繁需要在此结构上执行不同操作的场景。访问者模式通过将操作移动到一个访问者对象中,来保持元素对象的类结构不变。

角色

  1. 元素(Element):定义一个接受访问者对象的接口。
  2. 具体元素(Concrete Element):实现元素接口,存储该元素的信息。
  3. 访问者(Visitor):定义一个访问元素的接口,每个元素类都有一个对应的访问方法。
  4. 具体访问者(Concrete Visitor):实现访问者接口,存储访问元素时的状态,并定义元素访问的操作。

案例分析

假设我们有一个表示文档的结构,文档中包含不同类型的内容,比如文本段落和图片。我们希望对这些内容执行不同的操作,比如计算总字数或者显示内容,而不修改现有的文档结构。

Java实现

首先,我们定义元素接口和具体元素:

// 元素接口
public interface DocumentElement {
    void accept(Visitor visitor);
}

// 具体元素:文本段落
public class TextElement implements DocumentElement {
    private String content;

    public TextElement(String content) {
        this.content = content;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String getContent() {
        return content;
    }
}

// 具体元素:图片
public class ImageElement implements DocumentElement {
    private String url;

    public ImageElement(String url) {
        this.url = url;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String getUrl() {
        return url;
    }
}

然后,我们定义访问者接口和具体访问者:

// 访问者接口
public interface Visitor {
    void visit(TextElement textElement);
    void visit(ImageElement imageElement);
}

// 具体访问者:计算总字数
public class WordCountVisitor implements Visitor {
    private int wordCount;

    public WordCountVisitor() {
        wordCount = 0;
    }

    @Override
    public void visit(TextElement textElement) {
        String[] words = textElement.getContent().split("\\s+");
        wordCount += words.length;
    }

    @Override
    public void visit(ImageElement imageElement) {
        // 图片不计字数
    }

    public int getWordCount() {
        return wordCount;
    }
}

// 具体访问者:显示内容
public class DisplayVisitor implements Visitor {
    @Override
    public void visit(TextElement textElement) {
        System.out.println("Text: " + textElement.getContent());
    }

    @Override
    public void visit(ImageElement imageElement) {
        System.out.println("Image: " + imageElement.getUrl());
    }
}

最后,我们使用访问者模式:

public class Document {
    private List<DocumentElement> elements = new ArrayList<>();

    public void addElement(DocumentElement element) {
        elements.add(element);
    }

    public void displayAll() {
        for (DocumentElement element : elements) {
            element.accept(new DisplayVisitor());
        }
    }

    public int getWordCount() {
        return new WordCountVisitor().visitAll(elements);
    }

    private int visitAll(List<DocumentElement> elements, Visitor visitor) {
        for (DocumentElement element : elements) {
            element.accept(visitor);
        }
        return 0; // 实际上,我们需要一个方法来累加计算结果
    }

    public static void main(String[] args) {
        Document document = new Document();
        document.addElement(new TextElement("Hello, World!"));
        document.addElement(new ImageElement("http://example.com/image.png"));
        document.addElement(new TextElement("This is a test document."));

        System.out.println("Displaying all elements:");
        document.displayAll();

        System.out.println("Total word count: " + document.getWordCount());
    }
}

注意事项

  • 访问者模式使得增加新元素很容易,但增加新操作却需要修改所有的元素类,因此需要谨慎使用。
  • 访问者模式破坏了封装,因为元素需要接受访问者,暴露内部信息。
  • 访问者模式通常用于那些对象结构相对稳定,但经常需要在此结构上执行各种不同操作的场景。

通过上述案例分析,我们可以看到访问者模式如何在不改变现有对象结构的情况下,向对象添加新的操作。这种模式在某些特定场景下非常有用,但也需要考虑到其对封装性和扩展性的影响。

在上一个回答中,我们已经实现了访问者模式的一个基本例子,包括定义元素和访问者接口、具体实现以及如何在文档结构中应用访问者模式。接下来,我将提供一些额外的思考点和可能的改进方向。

改进方向

  1. 增加灵活性:考虑将访问者模式与命令模式结合使用,以提供更多的灵活性和可扩展性。

  2. 异步处理:如果访问操作非常耗时,可以考虑实现异步处理。

  3. 性能优化:在某些情况下,访问者模式可能会对性能产生影响,特别是当文档结构非常庞大时。需要考虑如何优化性能。

  4. 错误处理:在访问者模式中,错误处理非常重要。需要考虑如何处理访问过程中可能出现的异常。

  5. 访问者模式的变体:根据具体需求,可以考虑使用访问者模式的变体,如对称访问者模式或反向访问者模式。

访问者模式的变体

  1. 对称访问者模式:在这种变体中,访问者和元素是对称的,即访问者和元素都实现了相同的接口。这可以简化代码,但也牺牲了一些灵活性。

  2. 反向访问者模式:在这种变体中,元素对象访问访问者对象,而不是访问者访问元素。这在某些情况下可能更自然,但需要重新设计对象结构。

改进后的访问者模式实现

让我们对访问者模式实现进行一些改进,使其更加灵活和健壮:

public interface Visitor {
    void visitConcreteElementA(ConcreteElementA element);
    void visitConcreteElementB(ConcreteElementB element);
    // 可以添加更多的visit方法
}

public class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visitConcreteElementA(this);
    }

    // 其他具体元素的方法
}

public class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visitConcreteElementB(this);
    }

    // 其他具体元素的方法
}

public class ConcreteVisitor1 implements Visitor {
    @Override
    public void visitConcreteElementA(ConcreteElementA element) {
        // 处理ConcreteElementA
    }

    @Override
    public void visitConcreteElementB(ConcreteElementB element) {
        // 处理ConcreteElementB
    }

    // 可以添加更多的visit方法
}

public class ConcreteVisitor2 implements Visitor {
    @Override
    public void visitConcreteElementA(ConcreteElementA element) {
        // 另一种处理ConcreteElementA的方式
    }

    @Override
    public void visitConcreteElementB(ConcreteElementB element) {
        // 另一种处理ConcreteElementB的方式
    }

    // 可以添加更多的visit方法
}

public class ObjectStructure {
    private List<Element> elements = new ArrayList<>();

    public void addElement(Element element) {
        elements.add(element);
    }

    public void operate(Visitor visitor) {
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

使用改进后的访问者模式

public class Client {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.addElement(new ConcreteElementA());
        objectStructure.addElement(new ConcreteElementB());

        System.out.println("Operation 1:");
        objectStructure.operate(new ConcreteVisitor1());

        System.out.println("Operation 2:");
        objectStructure.operate(new ConcreteVisitor2());
    }
}

在这个改进的例子中,我们为访问者模式增加了更多的灵活性,允许根据不同的访问者实现不同的操作。同时,我们也提供了更多的扩展点,以便于在不修改现有结构的情况下添加新的操作。

通过这些改进,访问者模式变得更加健壮和易于使用。它不仅提供了更高的灵活性,还提高了代码的可维护性和可读性。在实际应用中,根据具体需求,还可以继续对访问者模式进行定制和优化。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/595722.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio实现简单的自定义钟表

项目目录 一、项目概述二、开发环境三、详细设计3.1、尺寸设置3.2、绘制表盘和指针3.3、动态效果 四、运行演示五、总结展望六、源码获取 一、项目概述 在安卓开发中&#xff0c;当系统自带的View已经无法满足项目需求时&#xff0c;就要自定义View。在Android中是没有与钟表有…

Linux PXE高效批量网络装机

系统初始化 systemctl disable --now firewalld.service setenforce 0 vim /etc/selinux/config 安装软件 yum install -y tftp-server xinetd dhcp vsftpd syslinux 复制 vmlinuz initrd.img pxelinux.0 到 /var/lib/tftpboot/ 目录 [rootlocalhost ~]# cd /mnt/…

OWASP 发布开源软件OSS的十大风险,已知漏洞排名第一,首次汇齐了10大类型的攻击案例

尽管软件供应链严重依赖开源软件&#xff0c;但业界缺乏一致的方法来理解和衡量开源软件的风险。 OSS&#xff08;Open Source Sofware&#xff0c;开源软件&#xff09;的风险管理从许可证管理开始&#xff0c;然后发展到CVE&#xff0c;但我们仍然缺乏涵盖安全、法律和运营方…

Java请求第三方接口的一些步骤

一、前言 Java请求第三方接口的一些步骤。 在Java中请求第三方接口通常涉及以下步骤。这些步骤涵盖了从准备请求到处理响应的整个过程。 1. 确定接口详情 接口URL&#xff1a;你要请求的URL。请求方法&#xff1a;如GET、POST、PUT、DELETE等。请求参数&#xff1a;包括URL…

git tag 相关操作

文章目录 前言一、git里面的Tag二、Tag 类型1. 轻量级标签&#xff08;Lightweight Tag&#xff09;2. 注释标签&#xff08;Annotated Tag&#xff09; 其他常用操作git taggit show <tagname>git push origin <tagname>删除tag使用 gitk 查看效果 总结 前言 一般…

SparkSQL编程入口和模型与SparkSQL基本编程

SparkSQL编程入口和模型 SparkSQL编程模型 主要通过两种方式操作SparkSQL&#xff0c;一种就是SQL&#xff0c;另一种为DataFrame和Dataset。 1)SQL&#xff1a;SQL不用多说&#xff0c;就和Hive操作一样&#xff0c;但是需要清楚一点的是&#xff0c;SQL操作的是表&#xf…

Day_1

1. 环境搭建 技术选型 后端项目结构 sky-take-out maven父工程&#xff0c;统一管理依赖版本&#xff0c;聚合其他子模块 sky-common 子模块&#xff0c;存放公共类&#xff0c;例如&#xff1a;工具类、常量类、异常类等 sky-pojo 子模块&#xff0c;存放实体类、VO、DTO…

【UE5学习笔记】编辑及运行界面:关闭眼部识别(自动曝光)

自动曝光&#xff0c;也就是走进一个黑暗的环境&#xff0c;画面会逐渐变量&#xff0c;以模拟人眼进入黑暗空间时瞳孔放大&#xff0c;进光量增加的一种真实视觉感受&#xff1a; 制作过程中是否关闭自动曝光&#xff0c;取决于游戏的性质&#xff0c;但是个人认为&#xff0c…

力扣每日一题115:不同的子序列

题目 困难 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 109 7 取模。 示例 1&#xff1a; 输入&#xff1a;s "rabbbit", t "rabbit" 输出&#xff1a;3 解释&#xff1a; 如下所示, 有 3 种…

遥遥领先们赚钱的路子,被香飘飘找到了……?

刚刚结束了的五一长假&#xff0c;中文互联网上可以说满是各种对立、冲突。 让人惋惜的胖猫及遭万人唾弃的捞女谭竹之外&#xff0c;曾经卖奶茶杯子绕地球几圈&#xff0c;如今却被多数人遗忘的香飘飘&#xff0c;一通操作下来&#xff0c;让不少吃瓜群众小刀剌屁股开了眼了……

自动化运维工具-Ansible

一、Ansible概述 Ansible是一种基于python开发的自动化运维工具&#xff0c;它只需要在服务端安装ansible&#xff0c;无需在每个客户端安装客户端程序&#xff0c;通过ssh的方式来进行客户端服务器的管理&#xff0c;基于模块来实现批量数据配置、批量设备部署以及批量命令执…

《Video Mamba Suite》论文笔记(4)Mamba在时空建模中的作用

原文翻译 4.4 Mamba for Spatial-Temporal Modeling Tasks and datasets.最后&#xff0c;我们评估了 Mamba 的时空建模能力。与之前的小节类似&#xff0c;我们在 Epic-Kitchens-100 数据集 [13] 上评估模型在zero-shot多实例检索中的性能。 Baseline and competitor.ViViT…

练习题(2024/5/5)

1左叶子之和 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24示例 2: 输入: root [1] 输…

【Web漏洞指南】XSS漏洞详细指南

【Web漏洞指南】XSS漏洞详细指南 概述XSS的三种类型执行任意 JS 代码的方式在原始HTML中注入绕过手法在 HTML标记内注入绕过手法在JavaScript代码中注入绕过手法其他绕过手法XSS常见有效载荷检索Cookies窃取页面内容键盘记录器查找内部IP地址端口扫描器自动填充密码捕获窃取 Po…

基于Spring Boot的大学生社团活动平台设计与实现

基于Spring Boot的大学生社团活动平台设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页功能界面图&#xff0c;学生在大学生社团…

Android11 InputManagerService启动流程分析

InputManagerService在systemserver进程中被启动 //frameworks\base\services\java\com\android\server\SystemServer.java t.traceBegin("StartInputManagerService"); inputManager new InputManagerService(context);//1 t.traceEnd(); //省略 //注册服务 Servi…

某东抢购某台脚本-低调

某东抢购某台脚本 小白操作-学习使用 注意&#xff1a; 本文部分变量已做脱敏处理&#xff0c;仅用于测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。技术层面需要提…

尊享面试100(272.最接近的二叉树搜索值|| python)

刚开始想着用最小堆&#xff0c;把每个元素都加进去&#xff0c;然后找出最小的k个值&#xff0c;复杂度应该是&#xff08;nklogn) import heapq as pq class Solution:def __init__(self):self.h []pq.heapify(self.h)def closestKValues(self, root: Optional[TreeNode], …

WINDOWS配置IIS

1.安装IIS 1.1.打开启用Windows功能 打开“控制面板” > “程序和功能” > “启用或关闭 Windows 功能”。 1.2.启用IIS功能 打开“控制面板” > “程序和功能” > “启用或关闭 Windows 功能”。 勾选“Internet Information Services”&#xff0c;然后点击“确定…

《21天学通C++》(第十一章)多态

为什么需要多态&#xff1f; 为了最大限度地减少代码&#xff0c;提高可读性 1.虚函数 虚函数是C中的一种特殊成员函数&#xff0c;它允许在派生类&#xff08;也称为子类&#xff09;中重写&#xff08;覆盖&#xff09;基类的实现&#xff0c;使用virtual进行声明 在C中&am…
最新文章