Visitor模式(訪問者設計模式)
Visitor ?
在Visitor模式中,資料結構與處理被分離開來。我們編寫一個表示“訪問者”的類來訪問資料結構中的元素, 並把對各元素的處理交給訪問者類。這樣,當需要增加新的處理時,我們只需要編寫新的訪問者,然後讓 資料結構可以接受訪問者的訪問即可。 **
概括: 資料結構與處理彼此分開,當需要實現新資料訪問方式的時候,實現Visitor就行了,(缺點:如果增加元素的訪問那會非常麻煩)
理清職責
作用:這裡用到Composition設計模式的那個檔案和資料夾的例子作為訪問者要訪問的資料結構。 地址:https://www.cnblogs.com/dgwblog/p/9840291.html 名字================>>>> 說明 | Visitor || 表示訪問者的抽象類,它訪問檔案和資料夾 |ELement || 表示資料結構的介面,它接受訪問者的訪問 |ListVisitor|visitor || 類的子類,顯示檔案和資料夾一覽 |Fi1e類和Directory || 類的父類,它是抽象類 |Entry(實現了Element介面) |File || 表示檔案的類 |Directory || 表示資料夾的類 |FileTreatementException || 表示向檔案中add時發生的異常的類 |Main ||測試程式行為的類
簡單說明:
Visitor與ELement 作用在你把程式碼閱讀以後會發現真的是非常簡單:如果說Composite中容器與內容一致性,這裡是還是將內容一致性維持著,但是將 那我們需要的資料結構的那部門的實現一套介面訪問者提取出來,實現的真正的訪問。
rootdir.acctep(new ListVisitor());
/*ListVisitor visitor = new ListVisitor();
visitor.visit(rootdir);*/
accept(接受)方法的呼叫方式如下。
element.accept(visitor);
而visit(訪問)方法的呼叫方式如下。
visitor.visit (element);
UML
時序圖:
- 對於Directory類的例項和Fi1e類的例項,我們呼叫了它們的accept方法
- 對於每一個Directory類的例項和File類的例項,我們只調用了一次它們的accept方法
- 對於ListvVisitor的例項,我們呼叫了它的visit(Directory)和visit(File)方法
處理visit(Directory)和visit(File)的是同一個ListVisitor的例項
Code
Entry :
public abstract class Entry implements Element { // 這裡實現的Element的目的 是便於在後面的Concreate類中Visitor進行訪問 /** * 1. 檔名 * 2. 檔案大小 * @return */ public abstract String getName(); public abstract int getSize(); /** * Directory 增加條目 * File 不能增加條目 */ public Entry add(Entry entry)throws FileTreatementException { throw new FileTreatementException(); } /** * 增加資料的遍歷方法itorator * @return */ public Iterator iterator() throws FileTreatementException{ throw new FileTreatementException(); } @Override public String toString() { return getName()+"("+getSize()+")"; } }
- Element、ListVisitor 、Visitor
public abstract class Visitor {
/**
* 作用: 這裡的方法的過載數量決定你資料結構中資料的引數
* 這裡我們需要訪問 File Directory
*/
abstract void visit(File file);
abstract void visit(Directory directory);
}
public interface Element {
void acctep(Visitor visitor);
}
public class ListVisitor extends Visitor {
private String currentDir="";
@Override
void visit(File file) {
System.out.println(currentDir+"/"+file);
}
/**
* 實現遞迴訪問結構
*/
@Override
void visit(Directory directory) {
System.out.println(currentDir+"/"+directory);
String saveDir=currentDir;
currentDir=currentDir+"/"+directory.getName();
try {
Iterator it = directory.iterator();
while(it.hasNext()){
Entry o = (Entry) it.next();
o.acctep(this);
}
currentDir =saveDir;
} catch (FileTreatementException e) {
e.printStackTrace();
}
}
}
- Directory,File
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 String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
public void acctep(Visitor visitor) {
visitor.visit(this);
}
}
public class Directory extends Entry {
private String name;
private List<Entry> directory=new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public Entry add(Entry entry) throws FileTreatementException {
directory.add(entry);
return this;
}
@Override
public String getName() {
return name;
}
/**
* getSize() | printList(String prefix)
*
* 都會遞迴去遍歷下面可能存在的 目錄或者檔案的子項
*/
@Override
public int getSize() {
int size=0;
Iterator<Entry> it = directory.iterator();
while (it.hasNext()){
// 這裡的Entry 可能是目錄 也可能是檔案
Entry next = it.next();
size+=next.getSize();
}
return size;
}
@Override
public Iterator iterator() throws FileTreatementException {
return directory.iterator();
}
@Override
public void acctep(Visitor visitor) {
visitor.visit(this);
}
}
- FileTreatementException ,
public class FileTreatementException extends Exception {
public FileTreatementException() {
}
public FileTreatementException(String message) {
super(message);
}
}
- MainT
public class MainT {
public static void main(String[] args) throws FileTreatementException{
System.out.println("start +++++++++++");
Directory rootdir=new Directory("root");
Directory bindir = new Directory("bin");
Directory tempdir = new Directory("temp");
Directory userdir = new Directory("user");
rootdir.add(bindir);
rootdir.add(tempdir);
rootdir.add(userdir);
bindir.add(new File("vi",1000));
bindir.add(new File("notepaid",15000));
rootdir.acctep(new ListVisitor());
/*ListVisitor visitor = new ListVisitor();
visitor.visit(rootdir);*/
}
}