- 引入訪問者模式
- 訪問者模式的實(shí)例
- 訪問者模式分析
引入訪問者模式
Visitor是訪問者的意思畏浆。
數(shù)據(jù)結(jié)構(gòu)中保存著元素腥刹。一般我們需要對元素進(jìn)行處理纪隙,那么處理元素的代碼放在哪里呢捷雕?最顯然的方法就是放在數(shù)據(jù)結(jié)構(gòu)的類中叁鉴,在類中添加處理的方法途样。但是如果有很多處理江醇,就比較麻煩了,每當(dāng)增加一種處理何暇,我們就不得不去修改表示數(shù)據(jù)結(jié)構(gòu)的類陶夜。
visitor模式就是用來解決這個問題的,visitor模式將數(shù)據(jù)結(jié)構(gòu)的定義和處理分離開裆站。也就是會新增一個訪問者的類条辟,將數(shù)據(jù)元素的處理交給訪問者類黔夭,這樣以后要新增處理的時候,只需要新增訪問者就可以了羽嫡。
visitor模式的實(shí)例
我們在這個實(shí)例中會結(jié)合composite模式[http://www.reibang.com/p/685dd6299d96]中的實(shí)例基礎(chǔ)上進(jìn)行增改纠修,文件夾和文件表示我們要訪問的數(shù)據(jù)結(jié)構(gòu),然后我們實(shí)現(xiàn)訪問者將文件夾和文件的內(nèi)容輸出厂僧。
先看一下類圖:
我們看到對于數(shù)據(jù)結(jié)構(gòu)那部分有一個接口element扣草,這個接口就是用來接受訪問者的一個接口,里面只有一個方法就是接受一個訪問者颜屠,然后子類具體的數(shù)據(jù)結(jié)構(gòu)回去實(shí)現(xiàn)這個這個方法辰妙。
package Visitor;
public interface Element {
public abstract void accept(Visitor v);
}
然后訪問者有個抽象類,根據(jù)不同類型的訪問處理甫窟,可以繼承這個方法添加密浑,這里的訪問者也可以用接口實(shí)現(xiàn)筆者認(rèn)為。
package Visitor;
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
訪問者定義了兩個抽象方法粗井,分別是用來訪問文件類和目錄類的
接下來看看具體的訪問者實(shí)現(xiàn):
package Visitor;
import java.util.Iterator;
public class ListVisitor extends Visitor {
private String currentdir = "";
public void visit(File file) {
System.out.println(currentdir + "/" + file);
}
public void visit(Directory directory) {
System.out.println(currentdir + "/" + directory);
String savedir = currentdir;
currentdir = currentdir + "/" + directory.getName();
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
entry.accept(this);
}
currentdir = savedir;
}
}
這里就是具體的處理邏輯代碼了尔破。
file類和directory類需要實(shí)現(xiàn)element接口,并實(shí)現(xiàn)accpet方法就是接受一個訪問者對象浇衬,并調(diào)用訪問者對象訪問自己
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);
}
}
package Visitor;
import java.util.Iterator;
import java.util.ArrayList;
public class Directory extends Entry {
private 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);
}
}
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 {
throw new FileTreatmentException();
}
public String toString() {
return getName() + " (" + getSize() + ")";
}
}
最后懒构,測試:
package Visitor;
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("hanako");
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();
}
}
}
測試結(jié)果:
visitor模式分析
我們分析一下訪問者模式示例程序的處理流程,假設(shè)一個文件夾下有兩個文件
- 首先耘擂,main類生成了listVisitor實(shí)例胆剧。
- Main類調(diào)用directory類的方法,并將生成的listVistor實(shí)例傳了進(jìn)去
- directory的accept方法會調(diào)用訪問者的處理方法醉冤,并將自身實(shí)例傳了進(jìn)去秩霍。
- 接下來,就來到了listVIsitor的處理流程中了蚁阳,訪問文件夾铃绒,然后找到文件夾里的第一個文件,對這個文件也調(diào)用accpet方法進(jìn)行訪問螺捐,傳進(jìn)去的對象是自身的listVIsitor實(shí)例
- 這是個遞歸的過程颠悬,listVisitor又會去訪問處理file,然后訪問完了就返回類似于堆棧
- 然后回到directory的處理中归粉,又接著處理第二個文件椿疗,重復(fù)上述過程,直到?jīng)]有可處理的對象了就會返回糠悼。
visitor模式中的角色:
visitor(訪問者)
訪問者角色負(fù)責(zé)對數(shù)據(jù)結(jié)構(gòu)中的每一個具體的元素聲明一個對應(yīng)的訪問的visit方法届榄,具體的實(shí)現(xiàn)則交給concretevisitor去實(shí)現(xiàn)ConcreteVisitor(具體的訪問者)
負(fù)責(zé)繼承實(shí)現(xiàn)visitor的訪問處理的方法,實(shí)現(xiàn)具體的處理邏輯Element
表示訪問者訪問的對象倔喂,聲明接受訪問者的accept方法铝条。ConcreteElemnet
負(fù)責(zé)實(shí)現(xiàn)具體的元素靖苇,實(shí)例中的file和directory類ObjectStructure
負(fù)責(zé)處理Elemnet元素的集合,每個Elemnet都實(shí)現(xiàn)了接收訪問者的方法班缰,所以為了訪問到所有的元素贤壁,需要存儲一個所有元素的集合結(jié)構(gòu),實(shí)例中的directory對應(yīng)于這個埠忘。
類圖: