访问者(Visitor)模式--------访问数据结构并处理数据

》》在Visitor 模式中,数据结构与处理被分离开来

-------------下面的示例程序中:文件与文件夹作为访问者要访问的数据结构,

                  访问者会访问文件与文件夹,然后显示出文件和文件夹的一览。

》》示例程序的类图:

        

》》Visitor类:

        package visitor;
/*
 * 访问者类
 */
public abstract class Visitor {
public abstract void visit(File file);                //访问文件
public abstract void visit(Directory directory);      //访问文件夹
}

      说明:该类是表示访问者的抽象类。访问者依赖于它所访问的数据结构

      (即 File 类和Directory 类 )

》》Element 接口;

       package visitor;
/*
 * Element 接口是接受访问者的访问的接口。
 */
public interface Element {
//声明 accept 方法
public abstract void accept(Visitor v);


}

      说明:该接口是接收访问者的访问的接口。

》》Entry 类:

        package visitor;


import java.util.Iterator;


public abstract class Entry implements Element{
public abstract String getName();                              //获取名字
public abstract int getSize();                                 //获取大小
public Entry add(Entry entry) throws FileTreatmentException{   //增加目录条目
throw new FileTreatmentException();
}

public Iterator iterator() throws FileTreatmentException{       //生成 Iterator
throw new FileTreatmentException();
}

public String toString(){                                      //显示字符串
return getName()+"("+getSize()+")";
}


}

》》File 类:

        package visitor;


public class File extends Entry{
private String name;                    //文件的名字
private int size;                       //文件的大小
public File(String name , int size){    //构造器
this.name = name;
this.size = size;
}
public String getName(){                 //获取文件的名字
return name;
}
public int getSize(){                    //获取文件的大小
return size;


public void accept(Visitor v){          //接收访问者
v.visit(this);
}


}

》》Directory 类:

        package visitor;
import java.util.ArrayList;
import java.util.Iterator;


public class Directory extends Entry{
public String name;                          //文件夹的名字
private ArrayList dir = new ArrayList();     //目录条目集合
public Directory(String name){                //构造函数
this.name = name;
}
public String getName(){                     //获取文件夹的名字
return name;
}
public int getSize(){                       //获取文件夹的大小
int size = 0;
Iterator it = dir.iterator();            //获取目录条目集合的迭代器
while(it.hasNext()){
Entry entry = (Entry)it.next();     //逐个获取集合中的元素
size += entry.getSize();
}
return size;
}

public Entry add(Entry entry){              //增加目录条目
dir.add(entry);                         //放在集合中
return this;
}

public Iterator iterator(){                 //生成 Iterator
return dir.iterator();
}

public void accept(Visitor v){             //接受访问者的访问
v.visit(this);
}
}

》》ListVisitor 类:

        package visitor;


import java.util.Iterator;


public class ListVisitor extends Visitor {
private String currentdir = "";               //当前访问的文件夹的名字
public void visit(File file) {                //访问文件夹时被调用
//像下面的字符串直接加上对象的引用,程序会自动调用该对象的 toString() 方法
System.out.println(currentdir  + "/"+file);
}
public void visit(Directory directory) {      //访问文件夹时被调用
System.out.println(currentdir + "/"+directory);
String savedir = currentdir;              //在savedir 中保存currentdir中原来的值
currentdir = currentdir+"/"+ directory.getName(); //currentdir 保存当前文件夹的路径
Iterator it = directory.iterator();
while(it.hasNext()){
Entry entry =(Entry)it.next();
entry.accept(this);
}
currentdir = savedir;
}


}

       说明:visit(File file)  这个方法是用来实现“对File 类的实例要进行的处理”;

          visit(Directory directory) 这个方法是用来实现“对 Directory  类的实例要进行的处理

       ******* 在 Visitor 模式中,则是 accept  方法与 visit 方法之间相互递归调用

》》FileTreatmentException 类:

         package visitor;
/*
 * 表示向文件中add时发生的异常的类
 */
public class FileTreatmentException extends RuntimeException{
public FileTreatmentException(){}
public FileTreatmentException(String msg){
super(msg);
}


}

》》Main 类------>测试程序

         package visitor;


import visitor.Directory;
import visitor.File;
import visitor.FileTreatmentException;


public class Main {
public static void main(String[] args){
try{
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",10000));
bindir.add(new File("latex",20000));
rootdir.accept(new ListVisitor());;

System.out.println("");
System.out.println("Making user entries...");
Directory yuki = new Directory("yuki");
Directory hanako = new Directory("hanakpo");
Directory tomura = new Directory("tomura");
usrdir.add(yuki);
usrdir.add(hanako);
usrdir.add(tomura);
yuki.add(new File("diary.html",100));
yuki.add(new File("Composite.java",200));
hanako.add(new File("memo.tex",300));
tomura.add(new File("game.doc",400));
tomura.add(new File("junk.mail",500));
rootdir.accept(new ListVisitor());;


}catch(FileTreatmentException e){
e.printStackTrace();
}
}


}

》》运行结果:

         

       通过上面的示例程序,在Visitor 模式中,visit 方法将“处理”集中在

        ListVisitor 里面了。

-------------------------------------------------------------------

       《Visitor 模式中的登场角色》

       **** Visitor (访问者)

              Visitor 角色负责对数据结构中每个具体的元素(ConcreteElement 角色)声明一个用于

        访问  xxx 的 visit(xxx) 的方法。 visit(xxx) 是用于处理 xxx 的方法,负责实现该方法的

         ConcreteVisitor 角色。在示例程序中,由 Visitor 类扮演此角色。

        **** ConcreteVisitor (具体的访问者)

                ConcreteVisitor 角色负责实现 Visitor 角色所定义的接口(API)。在示例程序中,由

          ListVisitor 类扮演此角色。

        **** Element (元素)

                Element 角色表示 Visitor 角色的访问对象。它声明了接受访问者的 accept 方法。

          accept 方法接收到的参数是 Visitor 角色。在示例程序中,由 Element 接口扮演此角色。

        ****  ConcreteElement 

               ConcreteElement 角色负责实现 Element 角色所定义的接口(API)。在示例程序中,

          由 File 类和 Directory 类扮演此角色。

       ****  ObjectStructure (对象结构)

              ObjectStructure 角色负责处理 Element 角色的集合。ConcreteVisitor 角色为每个Element

           角色都准备了处理方法。在示例程序中,由 Directory 类扮演此角色(一人分饰两角)。为了

         让 ConcreteVisitor 角色可以遍历处理每个 Element 角色,在示例程序中,我们在Directory

          类中实现了 iterator 方法。

-----------------------------------------------------

          《扩展思路的要点》

           1.双重分发

                 在Visitor 模式中,ConcreteVisitor 和 ConcreteElement  这两个角色共同决定了实际进行的

             处理。这种消息分发的方式一般被称为双重分发。

           2.为什么要弄得这么复杂

                   Visitor 模式的目的是:将处理从数据结构中分离出来。数据结构很重要,它能将元素集合和

               关联在一起。但是,需要注意的是,保存数据结构与以数据结构为基础进行处理是两种不同的

              东西。

                  在上面的示例程序中, Visitor 模式提高了  File 类和 Directory  类 作为组件的独立性。

           3.开闭原则------对扩展开放,对修改关闭

                   该原则主张类应当是下面这样的:

                   ****对扩展是开放的

                   ****对修改是关闭的

                   在设计类时,若无特殊理由,必须要考虑到将来可能会扩展类。绝不能毫无理由地禁止扩展类。

              这就是“对扩展是开放”的意思。

                   如果在每次扩展类时都需要修改现有的类就太麻烦了。所以我们需要在不用修改现有类的前提

              下能够扩展类,这就是“对修改是关闭”的意思。

                   对扩展开放、对修改关闭的类具有高可复用,可作为组件复用。设计模式和面向对象的目的正是

               为我们提供一种结构,可以帮助我们设计出这样的类。

           4.易于增加的 ConcreteVisitor 角色

                  使用Visitor 角色可以很容易地增加 ConcreteVisitor 角色。因为具体的处理被交给ConcreteVisitor

               角色负责,因此完全不用修改 ConcreteElement 角色。

           5.难以增加的   ConcreteElement  角色        

            6.Visitor 工作所需要的条件

                  “在 Visitor 模式中,对数据结构中的元素进行处理的任务被分离出来,交给 Visitor 类负责。”这样就

              实现了数据结构与处理的分离。但是要达到这个目的是有条件的,那就是 Element 角色必须向 Visitor

              角色公开足够多的信息。

                    访问者只有从数据结构中获取了足够多的信息后才能工作。如果无法获取到这些信息,它就无法

              工作。这样做的缺点是:如果公开了不应当被公开的信息,将来对数据结构的改良就会变得非常困难。

-----------------------------------------------------

              《相关的设计模式》

              1.Iterator 模式

              2.Composite 模式

              3.Interpreter 模式 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小达人Fighting

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值