圖解Java設計模式之訪問者模式

圖解Java設計模式之訪問者模式

測試系統的需求

1)將人分為男人和女人,對歌手進行測評,看完某個歌手表演後,得到他們對該歌手的不同評價(評價有不同的種類,比如成功、失敗等) 2)傳統方案

傳統方式的問題分析

1)如果系統比較小,沒有問題,但是考慮系統增加越來越多的新的功能時,對代碼改動比較大,違反類ocp原則,不利於維護。 2)擴展性不好,比如增加類新的人員類型,或者管理方法。

訪問者模式基本介紹

1)訪問者模式(Visitor Pattern),封裝一些作用於某種數據結構的各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。 2)主要將數據結構與數據操作分離,解決數據結構和操作耦合性問題 3)訪問者模式的基本工作原理是 :在被訪問的類裏面加一個對外提供接待訪問者的接口 4)訪問者模式主要應用場景是 :需要對一個對象結構中的對象進行很多不同操作(這些操作彼此沒有關聯),同時需要避免讓這些操作「污染」這些對象的類,可以選用訪問者模式解決。

說明 : 1)Visitor 是抽象訪問者,為該對象結構中的ConcreteElement的每一個類聲明一個visit操作。 2)ConcreteVisitor :是一個具體的訪問值,實現每個有Visitor 聲明的操作,是每個操作實現的部分。 3)ObjectStructure 能枚舉它的元素,可以提供一個高層的接口,用來允許訪問者訪問元素。 4)Element 定義一個accept方法,接收一個訪問者對象。 5)ConcreteElement 為具體元素,實現類accept方法。

訪問者模式應用實例

package com.example.demo.visitor;    import javax.activation.MailcapCommandMap;    public abstract class Action {    	/**  	 * 得到男性的測評  	 * @param man  	 */  	public abstract void getManResult(Man man);    	/**  	 * 得到女的測評  	 * @param woman  	 */  	public abstract void getWomanResult(Woman woman);    }  package com.example.demo.visitor;    public class Fail extends Action{    	@Override  	public void getManResult(Man man) {  		// TODO Auto-generated method stub  		System.out.println(" 男人給的評價該歌手很失敗 ! ");  	}    	@Override  	public void getWomanResult(Woman woman) {  		// TODO Auto-generated method stub  		System.out.println(" 女人給的評價該歌手很失敗 ! ");  	}    }  package com.example.demo.visitor;    public class Success extends Action{    	@Override  	public void getManResult(Man man) {  		// TODO Auto-generated method stub  		System.out.println(" 男人給的評價該歌手很成功 ! ");  	}    	@Override  	public void getWomanResult(Woman woman) {  		// TODO Auto-generated method stub  		System.out.println(" 女人給的評價該歌手很成功 ! ");  	}    }  package com.example.demo.visitor;    public abstract class Person {    	public abstract void accept(Action action);  }  package com.example.demo.visitor;    public class Man extends Person {    	@Override  	public void accept(Action action) {  		// TODO Auto-generated method stub  		action.getManResult(this);  	}    }  package com.example.demo.visitor;    /**   * 說明   * 1. 這裡我們使用到了雙分派,即首先在客戶端程序中,將具體狀態作為參數傳遞Woman中(第一次分派)   * 2. 然後Woman 類調用作為參數的 「具體方法」 中方法getWomanResult,同時將自己(this)作為參數傳入,完成第二次的分派   * @author zhaozhaohai   *   */  public class Woman extends Person {    	@Override  	public void accept(Action action) {  		// TODO Auto-generated method stub  		action.getWomanResult(this);  	}    }  package com.example.demo.visitor;    import java.util.LinkedList;  import java.util.List;    /**   * 數據結構,管理很多人(Man,Woman)   * @author zhaozhaohai   *   */  public class ObjectStructure {    	/**  	 * 維護一個集合  	 */  	private List<Person> persons = new LinkedList<Person>();    	/**  	 * 增加到list  	 * @param person  	 */  	public void attach(Person person) {  		persons.add(person);  	}    	/**  	 * 移除  	 * @param person  	 */  	public void detach(Person person) {  		persons.remove(person);  	}    	/**  	 * 顯示測試情況  	 * @param action  	 */  	public void display(Action action) {  		for (Person person : persons) {  			person.accept(action);  		}  	}  }  package com.example.demo.visitor;    public class Wait extends Action{    	@Override  	public void getManResult(Man man) {  		// TODO Auto-generated method stub  		System.out.println(" 男人給的評價是該歌手待定。。 ");  	}    	@Override  	public void getWomanResult(Woman woman) {  		// TODO Auto-generated method stub  		System.out.println(" 女人給的評價是該歌手待定。。 ");  	}    }  package com.example.demo.visitor;    public class Client {    	public static void main(String[] args) {  		// TODO Auto-generated method stub  		// 創建ObjectStructure  		ObjectStructure objectStructure = new ObjectStructure();  		objectStructure.attach(new Man());  		objectStructure.attach(new Woman());    		// 成功  		Success success = new Success();  		objectStructure.display(success);  		System.out.println("-------------------");  		// 失敗  		Fail fail = new Fail();  		objectStructure.display(fail);  		System.out.println("----------給的是待定的測評------------");  		Wait wait = new Wait();  		objectStructure.display(wait);  	}    }

小結 – 雙分派 上面提到雙分派,所謂雙分派是指不管類怎麼變化,我們都能找到期望的方法運行。雙分派意味着得到執行的操作取決於請求的種類和兩個接收者的類型。 假設要添加一個Wait的狀態類,考察Man類和Woman類的反應,由於使用了雙分派,只需增加一個Action子類即可在客戶端調用即可,不需要改動任何其他類的代碼。

訪問者模式的注意事項和細節

優點 : 1)訪問者模式符合單一職責原則,讓程序具有優秀的擴展性和靈活性非常高。 2)訪問者模式可以對功能進行統一,可以做報表、UI、攔截器與過濾器,適用於數據結構相對穩定的系統。 缺點 : 1)具體元素對訪問者公布細節,也就是說訪問者關注類其他類的內部細節,這是迪米特法則所不建議的,這樣造成類具體元素變更比較困難。 2)違背類依賴導致原則,訪問者依賴的是具體元素,而不是抽象元素。 3)因此,如果一個系統有比較穩定的數據結構,又有經常變化的功能需求,那麼訪問者模式就是比較合適的。