程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java設計模式(三) Visitor(訪問者)模式及多分派場景應用

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

編輯:JAVA綜合教程

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


基本概念

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 list = new ArrayList();
        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 list = new ArrayList();
        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來滿足新的訪問要求。因為數據和操作的分離,防止了添加新的操作污染原來的數據結構。

綜上

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

雙分派

首先介紹以下單分派

單分派:一個操作是根據請求者的名稱和接收到的參數決定的,在Java有靜態綁定動態綁定,分別是通過重載覆寫實現的。
雙分派:雙分派意味著得到執行的操作決定於請求的種類接收者的類型。正對應於訪問者模式

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved