設計模式之訪問者模式

0x01.定義與類型

  • 定義:將做用於某種數據結構中的各元素的操做分離出來封裝成獨立的類,使其在不改變數據結構的前提下能夠添加做用於這些元素的新的操做,爲數據結構中的每一個元素提供多種訪問方式。它將對數據的操做與數據結構進行分離,是行爲類模式中最複雜的一種模式。
  • 補充定義:能夠在不改變各元素的類的前提下,定義做用於這些元素的操做。
  • 類型:行爲型
  • UML類圖

visitor1.png

  • Java實現
/**
 * 訪問者接口
 */
public interface Visitor {

    /**
     * 訪問A
     * @param element
     */
    void visit(ConcreteElementA element);

    /**
     * 訪問B
     * @param element
     */
    void visit(ConcreteElementB element);

}

/**
 * 被訪問對象接口
 */
public interface Element {

    void accept(Visitor visitor);

    String operation();
}

/**
 * 具體的訪問者
 */
public class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("visit: " + element.operation());
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("visit: " + element.operation());
    }
}

/**
 * 具體實例A
 */
public class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    @Override
    public String operation() {
        return "ConcreteElementA";
    }
}

/**
 * 具體實例B
 */
public class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    @Override
    public String operation() {
        return "ConcreteElementB";
    }
}
  • 測試與應用
/**
 * 測試與應用類
 */
public class Test {

    public static void main(String[] args) {
        //初始化被訪問列表
        List<Element> elements = new ArrayList<>();

        //初始化訪問者
        Visitor visitor = new ConcreteVisitor();

        //初始化元素
        Element element1 = new ConcreteElementA();
        Element element2 = new ConcreteElementB();

        //裝填
        elements.add(element1);
        elements.add(element2);

        //訪問
        elements.forEach(e -> e.accept(visitor));
    }

}
  • 輸入結果
visit: ConcreteElementA
visit: ConcreteElementB
  • 訪問者中的元素html

    • 抽象訪問者(Visitor)角色:定義一個訪問具體元素的接口,爲每一個具體元素類對應一個訪問操做 visit() ,該操做中的參數類型標識了被訪問的具體元素。
    • 具體訪問者(ConcreteVisitor)角色:實現抽象訪問者角色中聲明的各個訪問操做,肯定訪問者訪問一個元素時該作什麼。
    • 抽象元素(Element)角色:聲明一個包含接受操做 accept() 的接口,被接受的訪問者對象做爲 accept() 方法的參數。
    • 具體元素(ConcreteElement)角色:實現抽象元素角色提供的 accept() 操做,其方法體一般都是 visitor.visit(this) ,另外具體元素中可能還包含自己業務邏輯的相關操做。
  • 注:測試類中,通常都是具備一個包含全部對象的容器,提供讓訪問者遍歷全部元素的方法。

0x02.適用場景

  • 一個數據結構如(List/Set/Map等)包含不少類型對象
  • 數據結構與數據操做分離

0x03.優缺點

1.優勢

  • 擴展性好。可以在不修改對象結構中的元素的狀況下,爲對象結構中的元素添加新的功能。
  • 複用性好。能夠經過訪問者來定義整個對象結構通用的功能,從而提升系統的複用程度。
  • 靈活性好。訪問者模式將數據結構與做用於結構上的操做解耦,使得操做集合可相對自由地演化而不影響系統的數據結構。
  • 符合單一職責原則。訪問者模式把相關的行爲封裝在一塊兒,構成一個訪問者,使每個訪問者的功能都比較單一。

2.缺點

  • 增長新的元素類很困難。在訪問者模式中,每增長一個新的元素類,都要在每個具體訪問者類中增長相應的具體操做,這違背了「開閉原則」。
  • 破壞封裝。訪問者模式中具體元素對訪問者公佈細節,這破壞了對象的封裝性,具體元素變動比較麻煩。
  • 違反了依賴倒置原則。訪問者模式依賴了具體類,而沒有依賴抽象類。

訪問者模式案例

使用訪問者模式,實現對不一樣種類課程的訪問封裝。
  • 具體實現
/**
 * 訪問者接口
 */
public interface IVisitor {

    void visit(FreeCourse freeCourse);

    void visit(CodingCourse codingCourse);
}

/**
 * 訪問者的具體實現
 */
public class Visitor implements IVisitor {
    @Override
    public void visit(FreeCourse freeCourse) {
        System.out.println("free course: " + freeCourse.getName());
    }

    @Override
    public void visit(CodingCourse codingCourse) {
        System.out.println("coding course: " + codingCourse.getName() + " price: " + codingCourse.getPrice());
    }
}

/**
 * 抽象類,課程
 */
public abstract class Course {

    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    abstract void accept(IVisitor visitor);

}


/**
 * 具體的實體類,免費課程
 */
public class FreeCourse extends Course {


    @Override
    void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

/**
 * 具體的實現類,實戰課程
 */
public class CodingCourse extends Course {

    private int price;

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}
  • 測試與應用類
/**
 * 測試與應用類
 */
public class Test {

    public static void main(String[] args) {
        //初始化容器
        List<Course> courses = new ArrayList<>();

        //構建課程實現
        FreeCourse freeCourse = new FreeCourse();
        freeCourse.setName("SpringMVC.");

        CodingCourse codingCourse = new CodingCourse();
        codingCourse.setName("Java design pattern.");
        codingCourse.setPrice(299);

        //裝填
        courses.add(freeCourse);
        courses.add(codingCourse);

        //訪問
        for (Course course : courses) {
            course.accept(new Visitor());
        }
    }
}
  • 輸入結果
free course: SpringMVC.
coding course: Java design pattern. price: 299
  • UML類圖

visitor2.png

0x05.相關設計模式

  • 訪問者模式和迭代器模式java

    • 訪問者,對保存在數據結構中的數據進行某種處理
    • 迭代器主要是遍歷

0x06.源碼中的訪問者模式

  • jdk.nio.FileVisitor
  • Spring.BeanDefinitionVisitor

0x07.代碼地址

0x08.推薦閱讀

相關文章
相關標籤/搜索