今天说说你不知道的访问者模式

简介: 今天说说你不知道的访问者模式

访问者模式

引入

在数据结构中保存着许多元素,我们会对这些元素进行处理。一般来说我们将处理的代码放在表示数据结构的类中。但是,如果处理有许多种呢?当每增加一种处理,我们就不得不取修改数据结构的类。

在Visitor模式中,**数据结构与处理被分离开来。**我们编写一个表示访问者的类来处理数据结构中的元素,并把个元素的处理交给访问者类。当增加新的处理时,我们只需要增加访问者类即可。

事例程序

类与接口

名字 说明
Visitor 访问者抽象类,访问文件和文件夹
Element 表示数据结构的接口,接受访问者访问
ListVisitor Visitor子类,显示文件和文件夹
Entity 抽象类,实现Element接口,File和Director父类
File 文件
Director 目录
Main 测试

类图

image.png

代码

Vivitor

  • 抽象访问者,访问者依赖于它所访问的数据结构
  • visit(File file) 访问文件
  • visit(Directory directory)访问目录
  • 重载方法,当从外部调用方法时,根据接收的参数自动选择执行相应的方法
public abstract class Visitor {
    public abstract void visit(File file);
    public abstract void visit(Directory directory);
}

Element

  • 接收访问者的接口
  • 声明accept方法,参数是Visitor
public interface Element {
    public abstract void accept(Visitor v);
}

Entity

  • 借用于组合模式
  • 实现Element接口,为了让Entity适用于Visitor模式。具体实现交由子类
public abstract class Entity implements Element {
    public abstract String getName();       //获取名字
    public abstract int getSize();          //获取大小
    public Entity add(Entity entity) throws Exception {
        throw new RuntimeException("运行异常");             //加入目录条目
    }
    public Iterator iterator() throws Exception {
        throw new RuntimeException();
    }
    @Override
    public String toString() {              //显示代表类文字
        return getName() + "(" + getSize() + ")";
    }
}

File

  • 组合模式中的File
  • 实现accept方法,使用 v.visit(this);此处的类型时File,所以调用的时visit(File)
public class File extends Entity {
    private String name;
    private int size;
    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getSize() {
        return size;
    }
    @Override
    public void accept(Visitor v) {
        v.visit(this);
    }
}

Director

  • iterator(),获取迭代器,方便遍历文件夹中所有的文件和文件夹
  • accept方法。此处调用的时v.visit(Director);(重载)
public class Directory extends Entity {
    private String name;
    private ArrayList<Entity> directory = new ArrayList();
    public Directory(String name) {
        this.name = name;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getSize() {
        int size = 0;
        Iterator<Entity> it = directory.iterator();
        while (it.hasNext()) {
            Entity entity = it.next();
            size += entity.getSize();
        }
        return size;
    }
    @Override
    public Entity add(Entity entity) throws Exception {
        directory.add(entity);
        return this;
    }
    public Iterator iterator() {
        return directory.iterator();
    }
    @Override
    public void accept(Visitor v) {
        v.visit(this);
    }
}

ListVisitor

  • Visitor的子类,它的作用是访问数据结构并显示元素一览
  • currentDir保存的是当前正在访问的文件夹名。
  • visit(File file)会被File类中的accept调用,参file是File的实例。也就是说,visit(File file)是用来实现对File类的实例要进行的处理。
  • 先显示当前文件夹的名字,然后获取iterator,通过iterator遍历文件夹中所有的目录条目并调用各自的accept方法。
  • accept方法调用visit方法,visit方法又调用accept方法。形成递归调用
public class ListVisitor extends Visitor {
    private String currentDir = ""; //当前访问文件夹的名字
    @Override
    public void visit(File file) {
        System.out.println(currentDir + "/" + file);
    }
    @Override
    public void visit(Directory directory) {
        System.out.println(currentDir + "/" + directory);
        String saveDir = currentDir;
        currentDir = currentDir + "/" + directory.getName();
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entity entity = (Entity) it.next();
            entity.accept(this);
        }
        currentDir = saveDir;
    }
}

Main

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("making root entries...");
        Directory rootDir = new Directory("root");
        Directory binDir = new Directory("bin");
        Directory tmpDir = new Directory("tmp");
        Directory usrDir = new Directory("usr");
        rootDir.add(binDir);
        rootDir.add(tmpDir);
        rootDir.add(usrDir);
        binDir.add(new File("vi", 100));
        binDir.add(new File("latex", 200));
        rootDir.accept(new ListVisitor());
        System.out.println();
        System.out.println("making user entries...");
        Directory yuki = new Directory("yuki");
        Directory hahako = new Directory("hahako");
        Directory tomura = new Directory("tomura");
        usrDir.add(hahako);
        usrDir.add(tomura);
        usrDir.add(yuki);
        yuki.add(new File("ddd.html", 100));
        yuki.add(new File("composite.java", 200));
        hahako.add(new File("memo.java", 300));
        tomura.add(new File("game.doc", 400));
        tomura.add(new File("junk.mail", 500));
        rootDir.accept(new ListVisitor());
    }
}

结果:

making root entries...
/root(300)
/root/bin(300)
/root/bin/vi(100)
/root/bin/latex(200)
/root/tmp(0)
/root/usr(0)
making user entries...
/root(1800)
/root/bin(300)
/root/bin/vi(100)
/root/bin/latex(200)
/root/tmp(0)
/root/usr(1500)
/root/usr/hahako(300)
/root/usr/hahako/memo.java(300)
/root/usr/tomura(900)
/root/usr/tomura/game.doc(400)
/root/usr/tomura/junk.mail(500)
/root/usr/yuki(300)
/root/usr/yuki/ddd.html(100)
/root/usr/yuki/composite.java(200)

时序图:

image.png

  • 对于Directory的实例和File的实例,我们调用他们的accept方法
  • 对于每一个Directory的实例和File的实例,我们只调用一个他们的accept方法
  • 对于ListVisitor实例,我们调用vist(Directory)和vist(File)方法
  • 处理vist(Directory)和vist(File)方法是同一个ListVisitor实例

登场角色

image.png

  • Visitor
    负责对数据结构中每个具体的元素声明同一个用于访问的visit方法。负责实现的是具体的访问者。(Visitor)
  • ConcreteVisitor
    具体的访问者,负责实现Visitor 所定义的接口。实现如何处理每个ConcreteElement角色。(ListVisitor)
  • Element
    表示Visitor角色的访问对象。声明接受访问者的accept方法,接受visitor参数。(Element)
  • ConcreteElement
    实现Element所定义的接口。(File和Directory)、
  • ObjectStructure(对象结构)(Directory)
    负责处理Element角色的集合。ConcreteVisitor为每个Element都准备好了处理方法。为了让ConcreteVisitor可以遍历处理到每个Element角色,在Directory中实现了iterator方法。
目录
相关文章
|
2月前
|
设计模式 编译器 C#
访问者模式
访问者模式是一种行为型设计模式,用于将数据结构与其操作解耦。通过在不改变数据结构的前提下增加新的操作,访问者模式提供了一种灵活的方式来实现功能扩展。其关键特性包括分离操作、支持扩展和双分派机制。适用于需要对对象结构中的元素执行多种操作、频繁扩展操作以及不希望操作逻辑和数据结构耦合的场景。常见的使用案例包括编译器中的语法树、文件系统和游戏场景。优点是增加操作时无需修改数据结构,符合开闭原则;缺点是添加新的数据结构类型时需要修改所有访问者类,维护成本较高。
53 3
|
6月前
|
设计模式 Java
访问者模式问题之在不使用访问者模式的情况下,怎么在一个 SelectNode 类中实现 extractFunctions 方法
访问者模式问题之在不使用访问者模式的情况下,怎么在一个 SelectNode 类中实现 extractFunctions 方法
|
设计模式 算法
行为型设计模式11-访问者模式
行为型设计模式11-访问者模式
59 1
|
9月前
|
算法
行为型 访问者模式
行为型 访问者模式
67 0
|
9月前
|
设计模式 uml C++
行为型 迭代器模式
行为型 迭代器模式
51 0
|
设计模式 算法 BI
设计模式~访问者模式(Visitor)-15
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对
58 0
|
设计模式 Java 关系型数据库
访问者模式解读
访问者模式解读
|
设计模式 程序员
访问者模式是啥?咋实现呀?
访问者模式是啥?咋实现呀?
|
设计模式
我学会了,访问者模式
访问者模式属于行为型模式,这个类型的设计模式总结出了 类、对象之间的经典交互方式,将类、对象的行为和使用解耦了,花式的去使用对象的行为来完成特定场景下的功能。
127 0
我学会了,访问者模式
|
设计模式 算法 Java
观察者模式和访问者模式(2)
观察者模式和访问者模式(2)
182 0
观察者模式和访问者模式(2)