1. 程式人生 > >Java設計模式(三) Visitor(訪問者)模式及多分派場景應用

Java設計模式(三) Visitor(訪問者)模式及多分派場景應用

public mod 項目 getname total college hide word dsm

基本概念

Visitor

  • 封裝一些作用於數據結構中的各元素的操作,不同的操作能夠借助新的visitor實現。減少了操作間的耦合性
  • 訪問者能夠將數據結構對數據的操作解耦,使得添加對數據結構的操作不須要取改動數據結構,也不必去改動原有的操作,而運行時再定義新的Visitor時閑著即可了(在操作加入上易拓展)

模式中角色分工

  • Visitor:抽象訪問者,在重載的visit函數中聲明訪問者能夠訪問的對象
  • Concrete Visitor:實現一個訪問者對於一個詳細的元素的操作
  • Element:抽象元素,聲明具有訪問該類型元素權限的訪問者的類型(通常是抽象類型)。提供重載的accept函數賦予權限
  • Concrete Element:實現accept方法,基本上是模板化的visitor.visit(this)
  • Object Structure:容納多種類型也許不同。接口或者不同的元素的集合。

例講Visitor的實現

先是一個簡單的樣例,展現一個最主要的簡陋的Visitor

既然在春招季。我們舉個簡歷篩選的樣例,投簡歷的都是寫本科生、專科生。還有碩士生、高職啊…為了簡單就先取前兩者。求職者的簡歷作為Element實現例如以下:

abstract class Student {
   //提供對於數據域基本操作的函數
    private String name;
    private
String university; private String rating; //讓指定的visitor獲得操作該對象的權限 public abstract void accept(Visitor visitor); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUniversity() { return
university; } public void setUniversity(String university) { this.university = university; } public String getRating() { return rating; } public void setRating(String rating) { this.rating = rating; } } class Bachelor extends Student{ @Override public void accept( Visitor visitor ) { visitor.visit( this ); } } class College extends Student{ @Override public void accept(Visitor visitor) { visitor.visit(this); } }

由於我們僅僅定義了兩種學生,所以接口提供了對於兩種Element訪問

interface Visitor{
    public void visit ( Bachelor bachelor );
    public void visit ( College college );
}

首先篩選簡歷我們看一下大家的簡歷都什麽樣子,那麽須要一個ShowVisitor:

class ShowVisitor implements Visitor {

    @Override
    public void visit(Bachelor bachelor) {
        System.out.println("A bachelor\n");
        //TODO 可能會有一些特異的操作,我們為了簡單就省略了
        this.printMessage( bachelor );
    }

    @Override
    public void visit(College college) {
        System.out.println(" a college student!\n");
        //TODO 同上
        this.printMessage( college );
    }

    public void printMessage ( Student student ){
        System.out.println( "Name : " + student.getName()+"\n"
                + "University : " + student.getUniversity()+"\n"
                + "Rating : " + student.getRating() + "\n"
        );
    }
}

要進行測試,我們首先要構造一個數據集合。也就是角色中相應的ObjectStructure,為了簡單我們直接用ArrayList了

public class VisitorEg {
    public static void main ( String [] args ){
        ArrayList<Student> list = new ArrayList<Student>();
        Bachelor bachelor = new Bachelor();
        bachelor.setName("llin");
        bachelor.setRating("100");
        bachelor.setUniversity("Tianjin University");

        College college = new College();
        college.setUniversity("Tianjin college");
        college.setRating("1");
        college.setName("dalinge");

        list.add ( bachelor );
        list.add ( college );

        Visitor visitor = new ShowVisitor();
        for ( Student student: list ){
            student.accept( visitor );
        }

    }
}

那麽好像看不出訪問者模式有什麽優勢啊!!!並且好費事啊,可是由於你將數據結構和對數據的操作分離了(解耦),所以當我想加入新的操作時,不須要改動原有的類,僅僅須要又一次實現一個visitor就能夠了。


所以,我們回到這個樣例。這麽多人報名,那麽究竟有多少本科生呢(假設人數夠了,可能直接偷懶僅僅面試本科生了),-_-萬惡的這樣的HR,所以我們須要一個統計的Visitor:

class SumVisitor implements Visitor{

    private int totalBachelor;

    SumVisitor(){
        super();
        totalBachelor = 0;
    }

    @Override
    public void visit(Bachelor bachelor) {
        totalBachelor++;
    }

    @Override
    public void visit(College college) {
    }

    public int getTotal_bachelor() {
        return totalBachelor;
    }
}

public class VisitorEg {
    public static void main ( String [] args ){
        ArrayList<Student> list = new ArrayList<Student>();
        Bachelor bachelor = new Bachelor();
        bachelor.setName("llin");
        bachelor.setRating("100");
        bachelor.setUniversity("Tianjin University");

        College college = new College();
        college.setUniversity("Tianjin college");
        college.setRating("1");
        college.setName("dalinge");

        list.add ( bachelor );
        list.add ( college );

        Visitor visitor = new ShowVisitor();
        Visitor visitor1 = new SumVisitor();
        for ( Student student: list ){
            student.accept( visitor );
            student.accept( visitor1);
        }
        System.out.println( "The total sum of bachelors : "+ ((SumVisitor)visitor1).getTotal_bachelor() );
    }
}

達到了要求。卻沒有改動一行代碼,開心!

Visitor應用場景

一定會有的疑問:visitor和iterator的差別:

  • visitor能夠訪問不同的對象(僅僅須要在Element定義相應的accept),可是Iterator僅僅能訪問同樣的對象。最起碼要有同樣的接口
  • iterator是不依賴詳細實現的,而visitor是依賴詳細實現的,由於Visitor會依據訪問的詳細的對象來採取相應的操作,而iterator最多僅僅是基於同樣的接口的泛化實現。
  • iterator訪問的數據結構的操作和數據並未分離。所以拓展功能起來須要改動,違反了開閉原則單一職責原則

    可是由於訪問者依賴詳細實現,而不是依賴抽象。所以違反了依賴倒置原則

優缺點決定的應用場景

  • 符合單一職責原則。功能上具有良好的拓展性,可是由於依賴詳細實現違背了詳細實現,所以為類的改動帶了麻煩。
  • 具有優良的拓展性。僅僅須要實現新的Visitor來滿足新的訪問要求。

    由於數據和操作的分離,防止了加入新的操作汙染原來的數據結構。

綜上

訪問者是一種集中規整模式,特別適合用於大規模重構的項目。在這一個階段的需求已經非常清晰,原系統的功能點也已經明白。通過訪問者模式能夠非常easy把一些功能進行梳理,達到終於目的功能集中化

雙分派

首先介紹下面單分派

單分派:一個操作是依據請求者的名稱和接收到的參數決定的,在Java有靜態綁定動態綁定,各自是通過重載覆寫實現的。


雙分派:雙分派意味著得到運行的操作決定於請求的種類接收者的類型

正相應於訪問者模式

Javac在構建、優化、解析語法樹的時候就是採用的是Visitor模式(語法、語義分析階段)

Java設計模式(三) Visitor(訪問者)模式及多分派場景應用