Java訪問者設計模式詳細講解

編程是一門藝術,大批量的改動顯然是非常醜陋的做法,用心的琢磨寫的代碼讓它變的更美觀。

在生活中,電影或電視劇中的人物角色,不同的觀眾對他們的評價也不同;還有顧客在商場購物時放在“購物車”中的商品,顧客主要關心所選商品的性價比,而收銀員關心的是商品的價格和數量。

這些被處理的數據元素相對穩定而訪問方式多種多樣的數據結構,如果用“訪問者模式”來處理比較方便。訪問者模式能把處理方法從數據結構中分離出來,並可以根據需要增加新的處理方法,且不用修改原來的程序代碼與數據結構,這提高瞭程序的擴展性和靈活性。

1.模式的定義

訪問者(Visitor)模式:是將作用於某種數據結構中的各元素的操作分離出來封裝成獨立的類,使其在不改變數據結構的前提下可以添加作用於這些元素的新的操作,為數據結構中的每個元素提供多種訪問方式。它將對數據的操作與數據結構進行分離,是行為類模式中最復雜的一種模式。

訪問者設計模式主要解決:java多態方法重載的靜態化問題。

2.訪問者設計模式的優點與不足

訪問者(Visitor)模式是一種對象行為型模式,其主要優點:

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

訪問者(Visitor)模式的不足:

  • 增加新的元素類很困難。在訪問者模式中,每增加一個新的元素類,都要在每一個具體訪問者類中增加相應的具體操作,這違背瞭“開閉原則”。
  • 破壞封裝。訪問者模式中具體元素對訪問者公佈細節,這破壞瞭對象的封裝性。
  • 違反瞭依賴倒置原則。訪問者模式依賴瞭具體類,而沒有依賴抽象類。

3.訪問者設計模式的實現思路

訪問者(Visitor)模式實現的關鍵是如何將作用於元素的操作分離出來封裝成獨立的類

訪問者模式包含以下主要角色。

  • 抽象訪問者(Visitor)角色:定義一個訪問具體元素的接口,為每個具體元素類對應一個訪問操作 visit() ,該操作中的參數類型標識瞭被訪問的具體元素。
  • 具體訪問者(ConcreteVisitor)角色:實現抽象訪問者角色中聲明的各個訪問操作,確定訪問者訪問一個元素時該做什麼。
  • 抽象元素(Element)角色:聲明一個包含接受操作 accept() 的接口,被接受的訪問者對象作為 accept() 方法的參數。
  • 具體元素(ConcreteElement)角色:實現抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業務邏輯的相關操作。
  • 對象結構(Object Structure)角色:是一個包含元素角色的容器,提供讓訪問者對象遍歷容器中的所有元素的方法,通常由 List、Set、Map 等聚合類實現。

4.訪問者設計模式實例

場景介紹:統計不同水果的價格,不同的水果放置到不同的集合,但是由於java多態中方法重載是靜態化的不足,導致統計不出來價格,就可以使用訪問者設計模式來處理。

public interface Fruit {
    int price();
    void draw();
    int accept(Visit visit);
}
public class Apple implements Fruit {
    private int price = 100;
    public Apple(){
    }
    public Apple(int price){
        this.price = price;
    }
    public void pack(AppleBag bag){
        bag.pack();
    }
    @Override
    public int price() {
        return price;
    }
    @Override
    public void draw() {
        System.out.print("蘋果紅富士");
    }
    public void setPrice(int price) {
        this.price = price;
    }
    public int accept(Visit visit){
        /*指針可以傳遞真實類型*/
        return visit.sell(this);
    }
}
public class Banana implements Fruit {
    private int price = 60;
    @Override
    public int price() {
        return price;
    }
    public void pack(BananaBag bag){
        bag.pack();
    }
    @Override
    public void draw() {
        System.out.print("仙人蕉");
    }
    public int accept(Visit visit){
        return visit.sell(this);
    }
    public void setPrice(int price) {
        this.price = price;
    }
}
public class Orange implements Fruit {
    private String name = "";
    private int price = 70;
    public Orange(String name,int price){
        this.price = price;
        this.name = name;
    }
    public void pack(OrangeBag bag){
        bag.pack();
    }
    @Override
    public int price() {
        return price;
    }
    @Override
    public void draw() {
        System.out.print("砂糖桔");
    }
    public int accept(Visit visit){
        return visit.sell(this);
    }
    public void setPrice(int price) {
        this.price = price;
    }
}
public class Visit {
	/*蘋果計價*/
	public int sell(Apple apple){
		System.out.println("apple's price: ¥50");
		return 50;
	}
	/*桔子計價*/
	public int sell(Orange orange){
		System.out.println("orange's price: ¥20");
		return 20;
	}
	/*香蕉計價*/
	public int sell(Banana banana){
		System.out.println("banana's price: ¥30");
		return 30;
	}
	//其它水果計價
	public int sell(Fruit fruit){
		System.out.println("other price: ¥10");
		return 10;
	}
}
public class VisitClient {
    private static Visit visit = new Visit();
    /*庫存*/
    private static List<Fruit> list = new ArrayList<>();
    static {
        list.add(StaticFactory.getFruitApple());
        list.add(StaticFactory.getFruitOrange());
        list.add(StaticFactory.getFruitBanana());
        list.add(StaticFactory.getFruitApple());
        list.add(StaticFactory.getFruitOrange());
    }
    private static int price() {
        int total = 0;
        for (Fruit fruit : list) {
            total += fruit.accept(visit);
        }
        System.out.println("總價值:" + total);
        return total;
    }
      public static void main(String[] args) {
        price();
    }

訪問者設計模式的關鍵是:不能直接使用visit對象直接調用sell方法將水果對象傳遞進去,因為直接傳遞進去由於方法重載的靜態化問題,不會執行相應的方法,需要是使用fruit的accept方法才行。

5.訪問者設計模式的使用場景

通常在以下情況可以考慮使用訪問者(Visitor)模式。

  • 對象結構相對穩定,但其操作算法經常變化的程序。
  • 對象結構中的對象需要提供多種不同且不相關的操作,而且要避免讓這些操作的變化影響對象的結構。
  • 對象結構包含很多類型的對象,希望對這些對象實施一些依賴於其具體類型的操作。

到此這篇關於Java訪問者設計模式詳細講解的文章就介紹到這瞭,更多相關Java訪問者模式內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: