十七、访问者模式-访问数据结构并处理数据 #和设计模式一起旅行#「建议收藏」

看过千山万水,依旧走不出自己的内心世界!故事背景Vistor : 访客,参观者,访问,本篇就讲讲Vistor模式,也就访问模式!有时候在软件开发中,我们会在一个数据结构中存放许多不同类型的对象信息,而且对每一个对象的处理方式并不相同,就存在数据结构对象内容的存放和数据的处理如何进行合理的设计! 如果将数据的处理和数据结构存放在一起,那么如果要新增一些对象信息的话,就需要修改…

大家好,又见面了,我是全栈君。

看过千山万水,依旧走不出自己的内心世界!

故事背景

Vistor : 访客,参观者,访问,本篇就讲讲Vistor模式,也就访问模式!

有时候在软件开发中,我们会在一个数据结构中存放许多不同类型的对象信息,而且对每一个对象的处理方式并不相同,就存在数据结构对象内容的存放数据的处理如何进行合理的设计! 如果将数据的处理和数据结构存放在一起,那么如果要新增一些对象信息的话,就需要修改数据结构,不利于程序的维护和扩展。在设计模式中,访问模式就是满足这种要求,将数据结构与处理分离开来,使用一个“访问者”类来访问数据结构中的元素,并把各元素处理交给访问类。

这样,当需要新增新的元素或者新增新的处理方式时候,就只需要编写新的访问者,然后让数据结构可以接受访问者的访问即可。

故事主角

访问者模式:提供一个作用于某对象结构中的各元素的操作表示,使得我们可以在不改变各元素的前提下定义作用于这些元素的新操作!

访问者类图

在访问者中有如下的几个角色:

  • Vistor(抽象访问者):抽象访问者为对象结构中每一个具体元素类ConcreteElement声明一个访问方法,这个操作的名称或参数可以清楚知道需要访问的具体元素的类型。

  • ConctreteVistor(具体的访问者):具体访问者实现了每个由抽象访问者声明的操作, 每一个操作用于访问对象结构中的一种类型。

  • Element(抽象元素):抽象元素一般是接口或者抽象类,它定义一个accept方法,该方法通常以抽象访问者为参数。

  • ConcreteElement(具体元素):具体元素实现了accpet方法,在accept方法中调用访问者的访问方法以便完成对一个元素的操作。

  • ObjectStructure(对象结构):对象结构是一个元素集合,它存放元素对象并且提供遍历内部元素的方法。

访问者模式包括了两个层次结构:
第一个是 访问者层次结构,提供抽象访问者和具体访问者;
第二个是 元素层次结构,提供抽象元素和具体元素。

简单代码 描述:

// 抽象访问者
public abstract class Visitor { 
   
    public abstract void visit(ConcreteElementA elementA);
    public abstract void visit(ConcreteElementB elementB);
}

// 具体访问者
public class ConcreteVisitor extends Visitor{ 
   
    public void visit(ConcreteElementA elementA)
    {
        //元素ConcreteElementA操作代码
    }
    public void visit(ConcreteElementB elementB)
    {
        //元素ConcreteElementB操作代码
    }
}

// 抽象元素
public interface Element{ 
   
    public void accept(Visitor visitor);
}
// 具体元素A
public class ConcreteElementA implements Element{ 
   
    public void accept(Visitor visitor){
        visitor.visit(this);
    }
    public void operationA()
    {
        // do something
    }
}
// 具体元素A
public class ConcreteElementB implements Element{ 
   
    public void accept(Visitor visitor){
        visitor.visit(this);
    }
    public void operationB(){
        // do something
    }
}
//对象结构
public class ObjectStructure { 
   
    private ArrayList<Element> list = new ArrayList<Element>(); //定义一个集合用于存储元素对象

    public void accept(Visitor visitor)
    {
        Iterator i=list.iterator();

        while(i.hasNext())
        {
            ((Element)i.next()).accept(visitor); //遍历访问集合中的每一个元素
        }
    }

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

    public void removeElement(Element element)
    {
        list.remove(element);
    }
}

通过上面简单代码可以看出,在Vistor模式中,accept和visit方法之间相互递归调用!
比如:在具体元素类A中的accept方法中,通过调用visit方法实现对元素的访问,并以当前对象作为visit方法的参数,具体执行过程:
(1) 调用具体元素类的accept(Visitor visitor)方法,并将Vistor子类对象作为参数;
(2)在具体元素类accept(Visitor visitor)方法内部调用传入的Visitor对象的visit()方法,如visit(ConcreteElementA elementA),将当前具体元素类对象(this)作为参数,如visitor.visit(this);
(3)执行Visitor对象的visit()方法,在其中还可以调用具体元素对象的业务方法。

这种调用机制也称为“双重分派”,正因为使用了双重分派机制,使得增加新的访问者无须修改现有类库代码,只需将新的访问者对象作为参数传入具体元素对象的accept()方法,程序运行时将回调在新增Visitor类中定义的visit()方法,从而增加新的元素访问方式。

武功修炼

本次例子在之前组合模式上进行,组合模式—— 容器与内容的一致性

程序简单示例类图:

程序代码类图

// 接口元素
public interface Element { 
   

    void accept(Visitor vistor);

}
// 抽象元素
public abstract class Entry implements Element{ 
   

    /** *获取名称 * @return */
    public abstract String getName();

    /** * 获取大小 * @return */
    public abstract int getSize();

    /** * 添加目录条目 * @param entry * @return * @throws FileTreatmentException */
    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException();
    }

    public Iterator iterator() throws FileTreatmentException{
        throw new FileTreatmentException();
    }
    @Override
    public String toString() {
        return getName() + "(" + getSize() + ")";
    }
}
//具体元素
public class Directory extends Entry { 
   

    private String name;// 文件夹名字

    private List directory = new ArrayList(); // 文件夹目录集合


    public Directory(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }


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

    @Override
    public int getSize() {
        int size = 0;
        Iterator it = directory.iterator();
        // 这里使用了迭代器模式
        while(it.hasNext()){
            Entry entry = (Entry) it.next();
            size += entry.getSize();
        }

        return size;
    }

    @Override
    public Entry add(Entry entry) throws FileTreatmentException {
        directory.add(entry);
        return this;
    }

    @Override
    public Iterator iterator() throws FileTreatmentException {
        return directory.iterator();
    }
}
//具体元素
public class File extends Entry { 
   

    private String name;
    private int size;

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

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

    @Override
    public String getName() {
        return name;
    }

    @Override
    public int getSize() {
        return size;
    }


}
//异常类
public class FileTreatmentException extends RuntimeException { 
   

    public FileTreatmentException(){}

    public FileTreatmentException(String msg){
        super(msg);
    }
}
//------------------
// 抽象访问者
public interface Visitor { 
   
    void visit(File file);

    void visit(Directory directory);
}
//具体访问着
public class ListVistor implements Visitor { 
   
    // 当前访问的文件夹名字
    private String currentDir = "";

    /** * 在访问文件时候调用 * @param file */
    @Override
    public void visit(File file) {
        System.out.println(currentDir + "/" + file);
    }

    /** * 访问文件夹的时候调用 * 双重分派 : 互相递归调用 * @param directory */
    @Override
    public void visit(Directory directory) {

        System.out.println(currentDir + "/" + directory);

        String saveDir = currentDir;
        currentDir = currentDir + "/" + directory.getName();

        Iterator iterator = directory.iterator();

        while (iterator.hasNext()){
            Entry entry = (Entry)iterator.next();
            entry.accept(this);
        }

    }
}
//数据结构
public class ObjectStructre { 
   

    private ArrayList<Element> list = new ArrayList<Element>(); //定义一个集合用于存储元素对象

    public void accept(Visitor visitor)
    {
        Iterator i=list.iterator();

        while(i.hasNext())
        {
            ((Element)i.next()).accept(visitor); //遍历访问集合中的每一个元素
        }
    }

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

    public void removeElement(Element element)
    {
        list.remove(element);
    }
}
// 测试类
public class TestClient { 
   

    public static void main(String[] args) {

        Directory rootDir = new Directory("root");
        Directory binDir = new Directory("bin");
        Directory confDir = new Directory("conf");
        Directory tempDir = new Directory("temp");

        rootDir.add(binDir);
        rootDir.add(confDir);
        rootDir.add(tempDir);

        binDir.add(new File("javac", 100));
        binDir.add(new File("jstac", 1000));

        confDir.add(new File("nginx.conf", 20));


        ObjectStructre os = new ObjectStructre();
        os.addElement(rootDir);

        os.accept(new ListVistor());

    }

}
/root(1120)
/root/bin(1100)
/root/bin/javac(100)
/root/bin/jstac(1000)
/root/bin/conf(20)
/root/bin/conf/nginx.conf(20)
/root/bin/conf/temp(0)

总结

  1. 学习双重分派(双重分发)的处理机制
  2. 在访问模式中,易于增加ConcreteVisitor角色,难以增加ConcreteElement角色
  3. 如果有数据结构与处理的分离,则可以试着考虑使用此模式
Next 期待下一篇吧!下一篇讲讲职责链模式!

参考

本专栏文章列表

一、设计模式-开篇—为什么我要去旅行? #和设计模式一起旅行#
二、设计模式-必要的基础知识—旅行前的准备 #和设计模式一起旅行#
三、设计模式介绍—她是谁,我们要去哪里? #和设计模式一起旅行#
四、单例模式—不要冒充我,我只有一个! #和设计模式一起旅行#
五、工厂模式—旅行的钱怎么来 #和设计模式一起旅行#
六、策略模式—旅行的交通工具 #和设计模式一起旅行#
七、观察者模式——关注我,分享旅途最浪漫的瞬间! #和设计模式一起旅行#
八、装饰者模式—巴厘岛,奶茶店的困扰! #和设计模式一起旅行#
九、命令模式—使用命令控制奶茶店中酷炫的灯 #和设计模式一起旅行#
十、模板方法模式—制作更多好喝的饮品! #和设计模式一起旅行#
十一、代理模式 —专注,做最好的自己!#和设计模式一起旅行#
十二、适配器模式——解决充电的烦恼 #和设计模式一起旅行#
十三、外观模式—— 简化接口 #和设计模式一起旅行#
十四、迭代器模式—— 一个一个的遍历 #和设计模式一起旅行#
十五、组合模式—— 容器与内容的一致性 #和设计模式一起旅行#
十六、状态模式—用类表示状态 #和设计模式一起旅行#
十七、访问者模式-访问数据结构并处理数据 #和设计模式一起旅行#
十八、职责链模式-推卸责任,不关我的事,我不管!#和设计模式一起旅行#
十九、原型模式—通过复制生产实例 #和设计模式一起旅行#
二十、设计模式总结—后会有期 #和设计模式一起旅行#


如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到,谢谢!

如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!


欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/121080.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • JAVA设计模式之单例模式

    本文继续介绍23种设计模式系列之单例模式。概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类只能有一个实例。  2、单例类必须自己创建自己的唯一实例。  3、单例类必须给所有其他对象提供这一实例。  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例…

  • 什么是Web 2.0——下一代软件的设计模式和商业模式 (全文翻译—1 博客版序)

    什么是Web 2.0——下一代软件的设计模式和商业模式 (全文翻译—1 博客版序)这篇文章,是经O’Reilly公司的TimO’Reilly先生授权后,进行全文翻译、出版的。包括:杂志节选版和网络全文版。转载:请遵循CC版权,保留原文作者和翻译作者信息,以及文章链接。感谢:互联网周刊的李洋提供了许多建议。几个文章版本链接:英文原文:http://www.oreillynet.com/pub/a/oreilly/tim/news/2005/09/30/

  • 从零开始的Android:常见的UI设计模式「建议收藏」

    从零开始的Android:常见的UI设计模式「建议收藏」尽管Android允许您创建几乎任何可能需要的自定义视图或用户界面,但事实证明,在正确的情况下,有一些用户界面模式可以很好地适用于用户。在本教程中,您将学习其中的一些模式,以及它们如何通过在使用应用程序时创造出色的体验来帮助用户。1.主画面用户在打开应用程序时看到的第一个屏幕通常是最重要的。从这里开始,您的用户应该能够执行快速动作并继续前进,或者进一步深入到您的应用中以完善他们…

  • 二、设计模式-必要的基础知识—旅行前的准备 #和设计模式一起旅行#[通俗易懂]

    设计模式-谈谈模式和设计模式模式(Pattern),指事物的标准样式,百度百科上面说的,其实说白了模式就是我们现在说的套路!模式 == 套路模式是一种思想,说大了特别的复杂和深奥,不管怎么样模式的使用可以解决特定场景下特定的问题!准确表达:模式是在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案。软件模式那么在软件中使用模式,就是软件模式(Sof…

  • visitor设计模式ppt_常用的设计模式

    visitor设计模式ppt_常用的设计模式动机Visitor是访问者的意思。数据结构中保存着元素。一般我们需要对元素进行处理,那么处理元素的代码放在哪里呢?最显然的方法就是放在数据结构的类中,在类中添加处理的方法。但是如果有很多处理,就比较麻烦了,每当增加一种处理,我们就不得不去修改表示数据结构的类。visitor模式就是用来解决这个问题的,visitor模式将数据结构的定义和处理分离开。也就是会新增一个访问者的类,将数据元素的处理交给访问者类,这样以后要新增处理的时候,只需要新增访问者就可以了。模式定义将更新(变更)封装到一个类中(访问

  • 设计模式、框架、架构、平台的区别「建议收藏」

    设计模式、框架、架构、平台的区别「建议收藏」区分什么是架构、框架、模式和平台,一直都感觉这几个词过于抽象和模糊,今天大家来说说到底什么是架构、框架、模式和平台? 收集了的一些来自网上各自的定义和区分如下: 设计模式 1、设计模式为什么要先说设计模式?因为设计模式在这些概念中是最基本的,而且也比较简单。那么什么是设计模式呢?说的直白点,设计模式就是告诉你针对特定问题如何组织类、对象和接口之间的关系,是前人总结的经验

    2022年10月10日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号