1. 程式人生 > >Visitor模式(訪問者設計模式)

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

    }
}