草庐IT

初识设计模式 - 访问者模式

程序员翔仔 2023-03-28 原文

简介

访问者设计模式(Visitor Design Pattern)的定义是,允许一个或多个操作应用到一组对象上,解耦操作和对象本身。

在使用访问者模式的时候,被访问的元素通常不是单独存在的,它们存储在一个集合中,这个集合称为“对象结构”,访问者通过遍历对象结构实现对其存储的元素进行逐个访问。

访问者模式使用了“双重分派”的调用机制,即元素对象定义一个操作方法支持注入访问者对象,在操作方法内调用访问者的访问方法,并将当前元素对象传入到访问方法中。

具体实现

在这里举一个工作当中的具体例子,在小公司的项目组当中,名义上区分了开发、测试等岗位,但实际上开发人员既要会开发,也有会测试,对于测试人员也是同样的要求,既要会测试,也要会开发。

在这里案例当中,开发人员、测试人员统称为元素,我们在这里先构建一个抽象的元素类。其代码示例如下:

public interface Element {
    // 定义一个接受访问者访问的抽象方法
    void accept(Visitor visitor);
}

对于开发人员类,根据自己的情况实现这个 accept() 方法,其代码如下:

public class Programmer implements Element {
    private String name = "开发人员";

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

对于测试人员,根据自己的情况实现这个 accept() 方法,其代码如下:

public class Tester implements Element {
    private String name = "测试人员";

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

第二步,最重要的就是要定义好一个访问者类,在这里抽象出的访问者接口可以是以项目组为范围,为项目组中的每一个元素定义对应的访问方法。其代码示例如下:

public interface Visitor {
    void visitProgrammer(Programmer programmer);

    void visitTester(Tester tester);
}

每当出现一个新的操作时,就可以实现访问者接口,注入不同的元素对象以实现不同的操作。

如下是开发人员和测试人员使用开发技能的代码示例:

public class DevelopVisitor implements Visitor {
    @Override
    public void visitProgrammer(Programmer programmer) {
        System.out.println(programmer.getName() + "在开发");
    }

    @Override
    public void visitTester(Tester tester) {
        System.out.println(tester.getName() + "在开发");
    }
}

如下是开发人员和测试人员使用测试技能的代码示例:

public class TestVisitor implements Visitor {
    @Override
    public void visitProgrammer(Programmer programmer) {
        System.out.println(programmer.getName() + "在测试");
    }

    @Override
    public void visitTester(Tester tester) {
        System.out.println(tester.getName() + "在测试");
    }
}

总结

优点

访问者模式的主要优点如下:

  • 能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能,符合开闭原则
  • 将有关元素的行为都封装到一个访问者对象中,每个访问者对象的功能都比较单一,符合单一职责原则

缺点

访问者模式的主要缺点如下:

  • 增加新的元素类需要在每一个访问者类中都增加相应的具体操作,这违背了开闭原则
  • 访问者对象可以访问并调用每一个元素对象的操作,这意味着元素对象有时候会暴露一些内部操作和内部状态,破坏了封装
  • 访问者模式依赖了具体类,而没有依赖抽象类,违反了依赖倒置原则

适用场景

访问者模式的适用场景如下:

  • 对象结构中元素对象的类很少改变,但经常需要在此对象结构上定义新的操作

源码

访问者模式提供一个方便的可维护的方式来操作一组对象,JDK 内置了这样的元素接口和访问者接口。

如下是元素接口 javax.lang.model.element.Element 的部分代码:

public interface Element extends javax.lang.model.AnnotatedConstruct {
    <R, P> R accept(ElementVisitor<R, P> v, P p);
}

如下是访问者接口 javax.lang.model.element.ElementVisitor 的部分代码:

public interface ElementVisitor<R, P> {
    R visit(Element e, P p);

    default R visit(Element e) {
        return visit(e, null);
    }
}

有关初识设计模式 - 访问者模式的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  6. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  7. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  8. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  9. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  10. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

随机推荐