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);*/ } }