Java二十三种设计模式-访问者模式(21/23)

news2024/11/15 13:26:12

本文深入探讨了访问者模式,一种允许向对象结构添加新操作而不修改其本身的设计模式,涵盖了其定义、组成部分、实现方式、使用场景、优缺点、与其他模式的比较,以及最佳实践和替代方案。

访问者模式:为对象结构添加新的操作

引言

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许在不修改对象结构的情况下,为对象结构添加新的操作。

基础知识,java设计模式总体来说设计模式分为三大类:

(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

 

第一部分:访问者模式概述

1.1 定义与用途

访问者模式的基本定义: 访问者模式是一种行为型设计模式,它允许你将算法与其所作用的对象结构分离,从而在不修改对象结构的情况下,为对象添加新的操作或行为。

访问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式。据《大话设计模式》中说算是最复杂也是最难以理解的一种模式了。

定义(源于GoF《Design Pattern》):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

从定义可以看出结构对象是使用访问者模式必备条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于Java语言当中的collection概念了。

为何需要访问者模式:

  • 分离算法与对象结构:当对象结构中的对象需要执行多种不同的操作时,访问者模式可以将这些操作封装在独立的访问者类中,实现算法与对象结构的分离。
  • 扩展性:如果对象结构经常变化,或者需要根据不同的条件执行不同的操作,访问者模式可以在不修改对象结构的情况下,灵活地添加新的操作。
  • 符合开闭原则:遵循软件设计的开闭原则,对扩展开放,对修改封闭。可以通过添加新的访问者类来扩展功能,而无需修改现有代码。

1.2 访问者模式的组成

元素(Element)

  • 定义:定义了接受访问者对象的接口,通常包含一个accept()方法,该方法接受一个访问者对象作为参数。

访问者(Visitor)

  • 定义:访问者接口定义了对每一个元素类进行访问的方法,每个方法都对应元素对象的一个接口。

对象结构(Object Structure)

  • 定义:对象结构通常是一个包含多个元素的复合对象,它允许访问者访问其包含的元素。

具体元素(Concrete Element)

  • 定义:实现元素接口的具体类,具体元素类实现访问者接口中定义的访问方法。

具体访问者(Concrete Visitor)

  • 定义:实现访问者接口的具体类,为每一个具体元素类提供相应的访问方法实现。

角色之间的交互

  • 接受访问:具体元素对象通过accept()方法接受一个访问者对象。
  • 访问元素:访问者对象通过调用元素的accept()方法,访问每一个具体元素对象。
  • 执行操作:访问者对象根据元素的不同类型执行相应的操作。

访问者模式通过将算法封装在访问者对象中,为对象结构提供了一种灵活的方式来添加新的操作,同时保持了对象结构的封闭性和可扩展性。在下一部分中,我们将通过Java代码示例来展示访问者模式的具体实现。

第二部分:访问者模式的实现

2.1 Java实现示例

以下是使用Java语言实现访问者模式的代码示例。假设我们有一个文档编辑器,它支持多种元素,如文本和图片,并且我们想要添加一个打印操作,用于打印文档中的每个元素。

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

// 具体元素:文本
class TextElement implements Element {
    private String content;

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

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

    public String getContent() {
        return content;
    }
}

// 具体元素:图片
class ImageElement implements Element {
    private String imageUrl;

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

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

    public String getImageUrl() {
        return imageUrl;
    }
}

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

// 具体访问者:打印访问者
class PrintVisitor implements Visitor {
    @Override
    public void visit(TextElement textElement) {
        System.out.println("Printing text: " + textElement.getContent());
    }

    @Override
    public void visit(ImageElement imageElement) {
        System.out.println("Printing image from: " + imageElement.getImageUrl());
    }
}

// 对象结构:文档
class Document {
    private List<Element> elements = new ArrayList<>();

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

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

// 客户端代码
public class Client {
    public static void main(String[]

 

2.2 访问者模式中的角色和职责

元素(Element)

  • 职责:定义一个accept()方法,允许访问者访问元素。

具体元素(Concrete Element)

  • 职责:实现元素接口,具体实现对访问者的操作。

访问者(Visitor)

  • 职责:定义访问者接口,声明访问具体元素的访问方法。

具体访问者(Concrete Visitor)

  • 职责:实现访问者接口,为每种类型的具体元素提供一个访问方法。

对象结构(Object Structure)

  • 职责:包含多个元素,允许访问者访问其包含的元素。

角色之间的相互作用

  • 元素接受访问者:具体元素通过调用accept()方法接受访问者的访问。
  • 访问者访问元素:访问者通过元素的accept()方法访问每个具体元素,并执行相应的操作。
  • 对象结构遍历元素:对象结构负责遍历其包含的元素,并将访问者传递给每个元素。

访问者模式通过将算法封装在访问者对象中,允许用户在不修改对象结构的情况下,为对象添加新的操作。这种模式在需要对多种不同类型的对象执行操作时非常有用。在下一部分中,我们将探讨访问者模式的使用场景。

第三部分:访问者模式的使用场景

3.1 对象结构需要扩展新操作的场景

在软件开发中,经常会遇到需要在已有的对象结构上添加新操作的需求。如果对象结构已经相当稳定,或者修改成本很高,访问者模式提供了一种理想的解决方案。

讨论在对象结构需要扩展新操作时,访问者模式的应用:

  • 无需修改现有类:通过访问者模式,可以在不修改现有对象结构中的类的情况下,为对象添加新的操作。
  • 灵活添加新行为:当对象结构需要支持多种不同的行为或操作时,访问者模式允许你灵活地添加新的行为,而不影响现有的功能。

应用实例:

  • 文档编辑器:在文档编辑器中,可能需要支持多种元素(如文本、图片、表格等),并可能在未来添加更多的元素类型或操作(如打印、导出等)。

3.2 对象结构复杂且不易修改的场景

当对象结构变得复杂,或者由于历史原因、多人协作开发等原因不易修改时,访问者模式可以有效地解决扩展性问题。

分析在对象结构复杂且不易修改的情况下,访问者模式的优势:

  • 保持对象结构封闭:访问者模式允许你在不改变现有对象结构的前提下,通过访问者类来扩展新功能。
  • 简化对象结构修改:当对象结构复杂时,直接修改对象结构可能会导致代码难以理解和维护,访问者模式提供了一种简化的扩展方式。
  • 降低耦合度:通过分离操作逻辑和对象结构,降低了它们之间的耦合度,使得系统更加模块化。

应用实例:

  • 复杂的软件系统:在一些复杂的软件系统中,如ERP系统、CRM系统等,对象结构可能非常复杂,且包含大量的业务逻辑。使用访问者模式可以在不修改现有系统的情况下,添加新的业务逻辑或报告功能。

访问者模式通过将操作逻辑封装在访问者对象中,为对象结构提供了一种灵活且解耦的方式来添加新操作。这种模式特别适合于对象结构稳定且需要动态扩展新功能的场景。在下一部分中,我们将讨论访问者模式的优点与缺点。

第四部分:访问者模式的优点与缺点

4.1 优点

提高灵活性

  • 动态添加行为:可以在不修改现有对象结构的情况下,通过添加新的访问者类来为对象添加新的行为。

保持对象结构的封闭性

  • 遵循开闭原则:对象结构对扩展开放,对修改封闭,允许在不改变现有代码的基础上扩展新功能。

降低耦合度

  • 分离算法与对象:将算法与对象结构分离,降低了它们之间的耦合度,提高了系统的模块化。

提高可维护性

  • 集中管理算法:所有与对象结构相关的操作都封装在访问者类中,便于集中管理和维护。

支持多态

  • 利用多态性:访问者模式通过多态性允许不同类型的元素对象响应同一操作。

4.2 缺点

增加系统复杂性

  • 类的数量增加:访问者模式可能会增加系统中类的数量,因为每个新操作都需要一个新的访问者类。

违反封装原则

  • 暴露元素接口:访问者模式要求元素对象暴露一个接受访问者的方法,这可能破坏对象的封装性。

增加设计难度

  • 理解难度:对于不熟悉访问者模式的开发者,理解整个模式的结构和意图可能有一定难度。

难以处理复杂操作

  • 操作复杂性限制:如果操作逻辑非常复杂,使用访问者模式可能会导致访问者类过于庞大和复杂。

性能考虑

  • 性能开销:在某些情况下,访问者模式可能会引入额外的性能开销,尤其是在访问者需要执行大量操作时。

访问者模式提供了一种强大的方法来为对象结构添加新的操作,同时保持对象结构的封闭性和可扩展性。然而,合理使用访问者模式并避免其缺点是至关重要的。了解其优点和缺点可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用访问者模式,以达到最佳的设计效果。

第五部分:访问者模式与其他模式的比较

5.1 与策略模式的比较

策略模式

  • 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互换。
  • 特点:策略模式关注于算法的封装和替换,通常用于多种算法或行为的动态选择。

访问者模式

  • 定义:访问者模式让你可以在不修改对象结构的情况下,为对象结构添加新的操作。
  • 特点:访问者模式关注于将操作从对象结构中分离出来,通过访问者对象来对对象结构中的元素执行操作。

对比

  • 封装内容:策略模式封装的是不同的算法或行为,访问者模式封装的是对对象结构的操作。
  • 使用场景:策略模式适用于需要根据不同条件选择不同算法的场景,访问者模式适用于需要在不修改对象结构的情况下添加新操作的场景。

5.2 与装饰者模式的对比

装饰者模式

  • 定义:装饰者模式允许你通过组合而非继承来动态地添加对象的额外职责。
  • 特点:装饰者模式关注于动态地扩展对象的功能,通常用于对象的包装和增强。

访问者模式

  • 定义:如前所述,访问者模式允许你为对象结构添加新的操作,同时保持对象结构的封闭性。

对比

  • 功能扩展方式:装饰者模式通过对象组合来扩展功能,访问者模式通过分离操作逻辑来扩展功能。
  • 目的:装饰者模式用于动态地添加额外职责,访问者模式用于在不修改对象结构的前提下添加新操作。

访问者模式和策略模式、装饰者模式都提供了处理对象行为的不同方法。每种模式都有其独特的用途和优势,选择使用哪种模式取决于具体的设计需求和场景。在下一部分中,我们将提供访问者模式的最佳实践和建议。

第六部分:访问者模式的最佳实践和建议

6.1 最佳实践

保持访问者和元素类的解耦

  • 解耦设计:确保访问者和元素类之间的耦合度最小,使得它们可以独立变化。

定义清晰的访问者接口

  • 接口明确:为访问者定义清晰的接口,明确每个访问者可以执行的操作。

避免在访问者中修改元素状态

  • 只读访问:尽量让访问者的操作是只读的,避免修改元素的状态,以保持元素的封装性。

使用访问者模式处理复杂的对象结构

  • 复杂结构适用:当对象结构复杂或包含多种不同类型的元素时,使用访问者模式可以更好地管理和操作这些元素。

考虑使用双分派

  • 双分派:利用Java中的双分派特性,通过访问者的visit方法实现对不同元素类型的操作。

6.2 避免滥用

避免过度使用访问者模式

  • 适度使用:只在确实需要为对象结构添加新操作且不便于修改对象结构时使用访问者模式。

避免在访问者中包含复杂逻辑

  • 简化逻辑:避免在访问者中包含过于复杂的逻辑,以免造成难以维护和理解的代码。

避免违反封装原则

  • 尊重封装:尽管访问者模式可能会要求元素类暴露某些接口,但应尽量避免破坏封装性。

6.3 替代方案

使用组合模式

  • 组合模式:在某些情况下,使用组合模式可以更自然地表达对象的部分-整体层次结构。

使用策略模式

  • 策略模式:如果操作的逻辑可以通过策略对象来实现,可以考虑使用策略模式来动态替换算法或行为。

使用命令模式

  • 命令模式:对于需要支持撤销或记录操作历史的场景,命令模式可能是一个更好的选择。

使用模板方法模式

  • 模板方法:如果算法的骨架可以在超类中定义,而某些步骤可以在子类中定制,可以考虑使用模板方法模式。

访问者模式是一种强大的设计模式,可以在不修改对象结构的前提下为对象添加新的操作。然而,合理使用访问者模式并避免其缺点是至关重要的。了解其替代方案可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用访问者模式,以达到最佳的设计效果。

结语

访问者模式提供了一种强大的方法来为对象结构添加新的操作,同时保持对象结构的封闭性和可扩展性。通过本文的深入分析,希望读者能够对访问者模式有更全面的理解,并在实际开发中做出合理的设计选择。


博主还写了其他Java设计模式关联文章,请各位大佬批评指正:

(一)创建型模式(5种):

Java二十三种设计模式-单例模式(1/23)

Java二十三种设计模式-工厂方法模式(2/23)

Java二十三种设计模式-抽象工厂模式(3/23)

Java二十三种设计模式-建造者模式(4/23)

Java二十三种设计模式-原型模式(5/23)

(二)结构型模式(7种): 

Java二十三种设计模式-适配器模式(6/23)

Java二十三种设计模式-装饰器模式(7/23)

Java二十三种设计模式-代理模式(8/23)

Java二十三种设计模式-外观模式(9/23)

Java二十三种设计模式-桥接模式(10/23)

Java二十三种设计模式-组合模式(11/23)

Java二十三种设计模式-享元模式(12/23)

 (三)行为型模式(11种): 

Java二十三种设计模式-策略模式(13/23)

Java二十三种设计模式-模板方法模式(14/23)

Java二十三种设计模式-观察者模式(15/23)

Java二十三种设计模式-迭代子模式(16/23)

Java二十三种设计模式-责任链模式(17/23)

Java二十三种设计模式-命令模式(18/23)

Java二十三种设计模式-备忘录模式(19/23)

Java二十三种设计模式-状态模式(20/23)

​欲知后事如何,且看下文分解...... ​

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑神话:悟空-配置推荐

显卡推荐&#xff08;按类别整理&#xff09; 1. GTX 10系列、GTX 16系列&#xff1a; 如果希望体验光线追踪&#xff0c;建议根据预算升级到RTX 40系列显卡。对于1080p分辨率&#xff0c;至少需要RTX 4060才能流畅运行。 2. RTX 20系列&#xff1a; RTX 2060、RTX 2070&#…

Openboxes 移动终端APP项目开发环境搭建与调试

文章目录 前言项目简介APP开发环境搭建APP开发环境启动及调试主应用程序启动及调试结语 前言 openboxes 项目还有一个针对移动端的项目&#xff1a;openboxes-mobile&#xff0c;但是这个项目的默认分支&#xff08;develop&#xff09;并没有与openboxes的默认分支对应&#…

LabVIEW优化内存使用

在LabVIEW中&#xff0c;优化内存使用的关键在于理解LabVIEW的内存管理机制并采用一些最佳实践。以下是一些可能帮助减少内存占用的方法&#xff1a; 1. 减少数据副本的生成 避免不必要的数据复制&#xff1a;每当你在程序中传递数组或子数组时&#xff0c;LabVIEW可能会创建副…

充电宝哪些品牌的性价比是最高的?开学最推荐入手四款充电宝

随着新学期的到来&#xff0c;学生们对充电宝的需求愈发迫切。无论是在校园内上课、图书馆自习&#xff0c;还是在外出游玩时&#xff0c;充电宝都成为了我们必不可少的随身装备。然而&#xff0c;市场上充斥着各种品牌和型号&#xff0c;如何选择一款性价比高的充电宝&#xf…

React+Vis.js(04):设置节点显示图片

文章目录 实现效果关键代码完整代码设置图片边框和背景颜色我们继续以 复仇者联盟为例,来介绍如何实现节点显示 图片。 实现效果 以图片进行节点的显示,使得显示效果更加直观,信息更为明了。 关键代码 在vis.js中,通过属性shape来控制节点显示为图像。 const nodes …

linux | 苹果OpenCL(提高应用软件如游戏、娱乐以及科研和医疗软件的运行速度和响应)

点击上方"蓝字"关注我们 01、引言 >>> OpenCL 1.0 于 2008 年 11 月发布。 OpenCL 是为个人电脑、服务器、移动设备以及嵌入式设备的多核系统提供并行编程开发的底层 API。OpenCL 的编程语言类似于 C 语言。其可以用于包含 CPU、GPU 以及来自主流制造商如 …

关于Hipe并发库中动态线程库DynamicThreadPond的一点解读(二)

文章目录 前提动态减少代码解读 动态增加线程池退出时发生了什么&#xff1f;总结附录 前提 我们在关于Hipe并发库中动态线程库DynamicThreadPond的一点解读(一)中介绍了DynamicThreadPond如何初始化&#xff0c;如何向任务队列中添加任务&#xff0c;线程池中的线程如何执行任…

机器学习——第十二章 计算学习理论

目录 12.1 基础知识 12.2 PAC学习 12.3 有限假设空间 12.3.1 可分情形 12.3.2 不可分情形 12.4 VC维 12.5 Rademacher复杂度 12.6 稳定性 12.1 基础知识 计算学习理论(computational learning theory)研究的是关于通过"计算"来进行"学习"的理论…

SAP无参考收货

其他收货 如果我们未参考其他凭证&#xff08;采购订单、生产订单或预留&#xff09;输入货物移动&#xff0c;则我们将讲到其他收货。因为在实际过帐之前&#xff0c;系统不会存储任何物料、数量、交货日期、接收工厂或来源方面的信息&#xff0c;所以此类收货属于计划外货物…

一文入门re 正则表达式

一、常用方法 &#xff08;一&#xff09;匹配 一般使用方法 第一个参数&#xff1a;正则模式 第二个参数&#xff1a;需要处理的字符串 第三个参数&#xff1a;附加处理方法result从任意位置开始匹配&#xff0c;返回match&#xff0c;没有匹配到返回None result re.searc…

MiniCPM-V: A GPT-4V Level MLLM on Your Phone论文阅读

大模型的趋势&#xff1a;模型性能越来越好&#xff0c;模型参数变小&#xff0c;端边设备计算能力变强。 MiniCPM-V优点 结果好、OCR能力突出、多分辨率、多语言、易于部署 模型结构 图片encoder适用vit。输入整体以及切片。切片使用自适应算法&#xff0c;通过计算分数&am…

揭秘图数据库:如何颠覆社交网络分析的游戏规则

在当今信息化时代&#xff0c;社交网络的数据量以指数级的速度增长&#xff0c;人们在社交网络中产生了海量的交互数据。如何从这些复杂的数据中提取有价值的信息&#xff0c;成为了数据科学和社交网络分析领域的一大挑战。图数据库作为一种有效的数据管理和分析工具&#xff0…

Anzo Capital视角下马丁格尔交易策略的利与弊

在Anzo Capital&#xff0c;我们深入探讨了马丁格尔交易策略的多面性。这种策略因其简单性而受到一些交易者的欢迎&#xff0c;但同时也存在着不可忽视的风险。本文将客观分析其优势和潜在缺陷&#xff0c;帮助投资者在Anzo Capital平台上做出更明智的决策。 马丁格尔策略的核…

JavaEE项目总结(1)

一、在vue项目中安装axios 由于需要使用axios框架进行异步请求&#xff0c;所以需要在vue项目中安装axios框架。在官方下载速度较慢&#xff0c;所以选择更换镜像源&#xff08;我使用的是华为云镜像&#xff09; 在项目终端中输入npm config set registry http://mirrors.…

MES系统:制造业转型升级的驱动力与效益源泉

制造业之所以需要并采纳MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;&#xff0c;主要是出于以下几个方面的考虑和需求。MES系统能够为企业带来显著的好处&#xff0c;具体体现在以下几个方面&#xff1a; 制造业需要MES系统的原因…

信贷风控架构一张图

记得刚入门风控时,由于做的模型岗,总有一种不能窥探风控全貌的疑惑。随着经验的积累,以及一些岗位的体验,慢慢对风控有了更清晰的认知。本文以一张图的形式简单地呈现信贷风控架构,让你能快速窥探信贷风控全貌。 文章目录 一、什么是风险管控?二、信贷风控架构一张图三、…

PCDN日常应用--公共资源利用

1、前言 PCDN 是通过在 CDN 的边缘节点上部署代理服务器&#xff0c;来实现主动调度和传输内容的方法。当用户请求内容时&#xff0c;PCDN 将根据各个节点的负载情况、距离、传输速度等一系列因素来动态选择最优的节点来提供内容。这样的调度方式使得 PCDN 在面对大规模请求时能…

健康管理系统解决方案

产品简介 脉购CRM健康管理系统是一款集会员健康管理以及会员数字化营销于一体的系统解决方案&#xff0c;旨在帮助企业更好地提供个性化的健康服务&#xff0c;维护好会员关系&#xff0c;通过有效的营销手段增加健康会员粘性和满意度&#xff0c;最终达到业绩增长的目的。 核…

云计算第三阶段---DBA Day2 -- Day4

DBA DAY2 一、常用函数、 常用函数 按使用方式分类按用途分类 ○单行函数 ○分组函数 ○字符函数 ○数学函数 ○日期函数 字符函数实例&#xff1a; ●LENGTH(str)&#xff1a;返字符串长度&#xff0c;以字节为单位 mysql> select length(abc); --------------- | l…

【NXP-MCXA153】TFT驱动移植

介绍 GC9A01是一款1.28寸的圆形TFT显示屏模块&#xff0c;分辨率为240x240&#xff0c;支持三种颜色格式&#xff1a;RGB444、RGB565、RGB666&#xff0c;本次实验将在NXP-MCXA153开发板上移植TFT LCD驱动 移植流程 ① 绑定TFT LCD上面的GPIO&#xff0c;如SCL_PIN、SDA_PIN…