0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

设计模式:访问者设计模式

冬至子 来源:墨风如雪小站 作者:墨风如雪 2023-06-06 11:25 次阅读

一、概述

1.1 定义

访问者设计模式是一种行为型设计模式,用于将算法与对象结构分离。它允许你在不改变对象结构的前提下定义新的操作。

1.2 作用

访问者模式的作用是在不改变对象结构的前提下定义新的操作。它允许你定义一个新的操作,而无需修改现有的对象结构。在访问者模式中,我们将操作封装在访问者对象中,并在元素对象上调用访问者对象的方法,从而实现对元素对象的操作。

1.3 适用场景

访问者模式适用于以下场景:

•对象结构稳定,但是经常需要在此结构上定义新的操作;•需要对复杂对象结构中的对象进行操作,而且这些对象可能具有不同的类型;•需要在不改变对象结构的前提下,为对象结构中的元素对象动态添加新的操作。

二、角色

2.1 抽象访问者(Visitor)

抽象访问者(Visitor)定义了访问者可以访问的元素对象的接口。它包含了多个 visit() 方法,每个方法对应一个具体元素对象。

publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}

在上述代码中,我们定义了一个抽象访问者接口 Visitor,它包含了两个 visit() 方法,分别对应具体元素对象 ConcreteElementA 和 ConcreteElementB。

2.2 具体访问者(ConcreteVisitor)

具体访问者(ConcreteVisitor)实现了抽象访问者接口,对不同类型的元素对象进行具体的操作。

publicclassConcreteVisitorAimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorA visit ConcreteElementA");
}


@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorA visit ConcreteElementB");
}
}


publicclassConcreteVisitorBimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorB visit ConcreteElementA");
}


@Overridepublicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorB visit ConcreteElementB");
}
}

在上述代码中,我们定义了两个具体访问者类 ConcreteVisitorA 和 ConcreteVisitorB,它们分别实现了抽象访问者接口 Visitor,并对不同类型的元素对象进行具体的操作。

2.3 抽象元素(Element)

抽象元素(Element)定义了元素对象的接口,让访问者对象可以访问自己的元素对象。

publicinterfaceElement{
void accept(Visitor visitor);
}

在上述代码中,我们定义了一个抽象元素接口 Element,它包含了一个 accept() 方法,该方法接收一个访问者对象作为参数

2.4 具体元素(ConcreteElement)

具体元素(ConcreteElement)实现了抽象元素接口,定义了自己的 accept() 方法,该方法调用访问者对象的 visit() 方法,并将自身作为参数传入。

publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
        visitor.visit(this);
}
}


publicclassConcreteElementBimplementsElement{
@Override
publicvoid accept(Visitor visitor){
        visitor.visit(this);
}
}

在上述代码中,我们定义了两个具体元素类 ConcreteElementA 和 ConcreteElementB,它们分别实现了抽象元素接口 Element,并在 accept() 方法中调用访问者对象的 visit() 方法,并将自身作为参数传入。

2.5 对象结构(Object Structure)

对象结构(Object Structure)是元素对象的集合,它提供了一个接口,让访问者对象可以访问集合中的元素对象。

publicclassObjectStructure{
privateList< Element > elements =newArrayList<  >();


publicvoid attach(Element element){
        elements.add(element);
}


publicvoid detach(Element element){
        elements.remove(element);
}


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

在上述代码中,我们定义了一个对象结构类 ObjectStructure,它包含了一个元素对象的集合 elements,提供了 attach() 和 detach() 方法,用于添加和删除元素对象。它还提供了一个 accept() 方法,该方法遍历元素对象集合,并调用每个元素对象的accept() 方法,将访问者对象作为参数传入。

三、实现步骤

3.1 创建抽象元素类

publicinterfaceElement{
void accept(Visitor visitor);
}

3.2 创建具体元素类

publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
        visitor.visit(this);
}
}


publicclassConcreteElementBimplementsElement{
@Override
publicvoid accept(Visitor visitor){
        visitor.visit(this);
}
}

3.3 创建抽象访问者类

publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}

3.4 创建具体访问者类

publicclassConcreteVisitorAimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorA visit ConcreteElementA");
}


@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorA visit ConcreteElementB");
}
}


publicclassConcreteVisitorBimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorB visit ConcreteElementA");
}


@Override
    publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorB visit ConcreteElementB");
}
}

3.5 创建对象结构类

publicclassObjectStructure{
privateList< Element > elements =newArrayList<  >();


publicvoid attach(Element element){
        elements.add(element);
}


publicvoid detach(Element element){
        elements.remove(element);
}


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

3.6 客户端调用

publicclassClient{
publicstaticvoid main(String[] args){
ObjectStructure objectStructure =newObjectStructure();
        objectStructure.attach(newConcreteElementA());
        objectStructure.attach(newConcreteElementB());


Visitor visitorA =newConcreteVisitorA();
Visitor visitorB =newConcreteVisitorB();


        objectStructure.accept(visitorA);
        objectStructure.accept(visitorB);
}
}

在上述代码中,我们创建了一个对象结构 objectStructure,并向其中添加了两个元素对象 ConcreteElementA 和 ConcreteElementB。然后,我们创建了两个具体访问者对象 visitorA 和 visitorB,并调用 objectStructure 的 accept() 方法,将这两个访问者对象作为参数传入。在 accept() 方法中,我们遍历元素对象集合 elements,并依次调用每个元素对象的 accept() 方法,将访问者对象作为参数传入。

四、优缺点

4.1 优点

访问者设计模式的优点包括:

•可以在不改变对象结构的前提下定义新的操作;•可以将代码的稳定性和易于扩展性相结合;•可以使得增加新的操作变得简单。

4.2 缺点

访问者设计模式的缺点包括:

•增加新的元素对象比较困难;•元素对象的访问者接口必须稳定,否则会导致所有访问者对象都需要进行修改;•具体元素对象对访问者对象的访问是单向的,访问者对象无法访问元素对象的其他方法。

五、扩展点

5.1 双重分派

在访问者设计模式中,我们可以通过双重分派来实现不同的操作。双重分派是指在访问者对象中定义了多个具体的 visit() 方法,每个方法对应一个具体元素对象,然后在元素对象中调用访问者对象的 visit() 方法,并将自身作为参数传入。

双重分派的实现方式是通过重载 accept() 方法来实现的。具体来说,我们在抽象元素接口 Element 中定义多个 accept() 方法,每个方法对应一个具体访问者对象,并在具体元素对象中重载这些 accept() 方法,将具体访问者对象作为参数传入。

下面是一个双重分派的示例代码:

publicinterfaceElement{
void accept(Visitor visitor);
void accept(Visitor visitor,String param);
}


publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
        visitor.visit(this);
}


@Override
publicvoid accept(Visitor visitor,String param){
        visitor.visit(this, param);
}
}


publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
void visit(ConcreteElementA elementA,String param);
}


publicclassConcreteVisitorAimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorA visit ConcreteElementA");
}


@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorA visit ConcreteElementB");
}


@Override
publicvoid visit(ConcreteElementA elementA,String param){
System.out.println("ConcreteVisitorA visit ConcreteElementA with param "+ param);
}
}


publicclassConcreteVisitorBimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorB visit ConcreteElementA");
}


@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorB visit ConcreteElementB");
}


@Override
publicvoid visit(ConcreteElementA elementA,String param){
System.out.println("ConcreteVisitorB visit ConcreteElementA with param "+ param);
}
}


publicclassObjectStructure{
privateList< Element > elements =newArrayList<  >();


publicvoid attach(Element element){
        elements.add(element);
}


publicvoid detach(Element element){
        elements.remove(element);
}


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


publicvoid accept(Visitor visitor,String param){
for(Element element : elements){
            element.accept(visitor, param);
}
}
}


publicclassClient{
publicstaticvoid main(String[] args){
ObjectStructure objectStructure =newObjectStructure();
        objectStructure.attach(newConcreteElementA());
        objectStructure.attach(newConcreteElementB());


Visitor visitorA =newConcreteVisitorA();
Visitor visitorB =newConcreteVisitorB();


        objectStructure.accept(visitorA);
        objectStructure.accept(visitorB);
        objectStructure.accept(visitorA,"paramA");
        objectStructure.accept(visitorB,"paramB");
}
}

在上述代码中,我们定义了一个新的 accept() 方法,该方法接收一个额外的参数 param,并在具体元素对象的 accept() 方法中将该参数传递给访问者对象的 visit() 方法。然后,我们创建了两个具体访问者对象 visitorA 和 visitorB,并分别调用 objectStructure 的 accept() 方法,将这两个访问者对象作为参数传入,并在第二个 accept() 方法中传递了参数 "paramA" 和 "paramB"。在访问者对象的 visit() 方法中,我们可以根据传递的参数进行不同的操作。

双重分派的优点是可以根据传递的参数实现不同的操作,从而增强了访问者模式的灵活性和扩展性。

然而,双重分派也有一些缺点。首先,它会导致类的层次结构变得复杂,因为每个具体元素对象都需要实现多个 accept() 方法。其次,双重分派会增加代码的复杂性,因为需要在访问者对象中定义多个具体的 visit() 方法,并在具体元素对象中重载多个 accept() 方法。最后,双重分派可能会导致代码的可读性变差,因为需要理解多个层次的调用关系。

因此,在使用双重分派时需要谨慎考虑,避免过度使用,以免导致代码的复杂性和可维护性下降。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Elements
    +关注

    关注

    0

    文章

    6

    浏览量

    5267
收藏 人收藏

    评论

    相关推荐

    如何判断访问者的浏览器是否打开了JAVASCRIPT功能?

    如何判断访问者的浏览器是否打开了JAVASCRIPT功能?作者:Hunte | 收录日期 : 2000-08-21   用这段代码就可以了
    发表于 01-11 09:33

    LabVIEW主/从设计模式和生产/消费设计模式

    点击学习>>《龙哥手把手教你学LabVIEW视觉设计》视频教程本教程将介绍 主/从设计模式与生产/消费设计模式(Master/Slave design pattern
    发表于 12-30 14:12

    状态机模式与生产/消费模式的关系

    原来有些明白,但是现在又有点糊涂了 ,状态机模式和生产/消费模式之间到底有没有关系?有的话到底是什么样子的关系?求大神解释!!!!!
    发表于 03-04 11:37

    LabVIEW结构设计实例:生成波形和分析波形(生产消费模式-事件 ;轮询模式

    LabVIEW结构设计实例:生成波形和分析波形(生产消费模式-事件 ;轮询模式
    发表于 07-23 11:59

    labview生产与消费模式

    想设计一个程序,传感器检测到质量大于多少开始运作,到一定质量再停止,用生产消费模式,编到这里不会编了。。。求教,谢谢
    发表于 03-11 14:42

    labview的生产/消费模式

    生产/消费模式以前在没有学习队列这块,看到生产/消费模式的时候总认为很困难。今天仔细学习
    发表于 05-05 09:36

    ARM Linux:usr模式转为svc模式的实现原理

    大家都知道linux的应用程序要想访问内核必须使用系统调用从而实现从usr模式转到svc模式。下面咱们看看它的实现过程。
    的头像 发表于 06-20 10:23 3505次阅读

    配置Nginx访问日志

    每当处理客户请求时,Nginx都会在访问日志中生成一个新记录。每个事件记录都包含一个时间戳,并包含有关客户端和所请求资源的各种信息。访问日志可以显示访问者的位置,访问者
    的头像 发表于 05-24 09:59 1993次阅读

    嵌入式软件设计模式 好文值得收藏

    本文引用自本人公众号文章: 嵌入式开发中的两点编程思想   C语言也很讲究设计模式?一文讲透   包含如下: 01)C语言和设计模式(继承、封装、多态)   02)C语言和设计模式访问者
    的头像 发表于 06-20 09:09 1683次阅读

    四种方式来让您以简单模式访问迁移器

    在《库迁移系列 - 前言》中,主要介绍了什么是库迁移器。DigiPCBA专用的库迁移器界面会自动分析您所选择的本地库并将它们迁移到您登录的工作区,实现一键式解决方案。库迁移器可以提供极简的简单界面模式和高级模式两种模式,文中介绍
    的头像 发表于 08-05 10:05 690次阅读

    GoF设计模式访问者模式

    访问者模式的目的是,解耦数据结构和算法,使得系统能够在不改变现有代码结构的基础上,为对象新增一种新的操作。
    的头像 发表于 10-08 11:05 499次阅读

    设计模式行为型:访问者模式

    访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。
    的头像 发表于 06-07 15:11 547次阅读
    设计<b class='flag-5'>模式</b>行为型:<b class='flag-5'>访问者</b><b class='flag-5'>模式</b>

    UVM设计模式访问者模式

    Visitor Pattern: 允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。换言之,如果component的数据结构是比较稳定的,但其是易于变化的,那么使用访问者模式是个不错的选择。
    的头像 发表于 08-11 09:28 453次阅读
    UVM设计<b class='flag-5'>模式</b>之<b class='flag-5'>访问者</b><b class='flag-5'>模式</b>

    设计模式中代理模式的使用场景

    官方定义: 代理模式 (Proxy Pattern) 是一种结构型设计模式,通过代理对象控制对原对象的访问,并允许在访问前或访问后做一些处理
    的头像 发表于 10-08 14:34 399次阅读
    设计<b class='flag-5'>模式</b>中代理<b class='flag-5'>模式</b>的使用场景

    php设计模式和应用场景

    ,从小型的网站到大型的企业级应用程序。 下面将介绍一些常见的PHP设计模式及其应用场景。 单例模式(Singleton Pattern): 单例模式用于限制类的实例化,确保一个类只有一个实例,并提供一个全局
    的头像 发表于 12-04 14:54 310次阅读