雙重分發(Double Dispatch)
什麼是雙重分發?
談起面向對象的程序設計時,常說起的面向對象的「多態」,其中關於多態,經常有一個說法是「父類引用指向子類對象」。
這種父類的引用指向子類對象的寫法類似下面這種:
Animal animal = new Dog(); animal.bark();
另一種常用的形式是
public class Keeper { public void say(Animal a) { System.out.println("Animal say"); } public void say(Dog dog) { System.out.println("dog say"); } } Animal animal = new Animal(); Animal dog = new Dog(); Keeper keeper = new Keeper(); keeper.say(animal); keep.say(dog);那上面的keeper調用兩次say的時候,會輸出什麼內容呢?會調用到兩個不同的方法嗎?
實際上在這兩次調用的時候,都會調用到say(Animal a)這個方法。由於這些內容在編譯期就能確實下來,這就是Java 的靜態分發。
從上面的圖我們看到,對於兩次調用生成的字節碼,確實都指向了say(Animal a)這個方法,運行時直接執行方法,輸出了對應的內容。
那對應的animal.bark() 為什麼最終會調用到dog 類的方法?這是在運行時確定具體的方法接收者的類型並執行。這就是所謂的動態分發,在運行時確定具體的方法,實現面向對象的多態。
分發(Dispatch)
分發就是指最終確定一個要執行的方法的過程。
對於Java 等靜態語言來說,都是通過單一分發(Single Dispatch)來進行的方法執行。
比如這樣一行代碼
dog.eat(new Bone())
最終執行要選擇的eat方法,只會根據dog的具體類型選擇到對應的方法,而傳入的參數並不能影響到對應方法的選擇,這種就是single Dispatch
為了讓傳入的真實參數,這裡就是Bone來真正起到作用,就需要用到Double Dispatch或者叫做Multiple Dispatch
也就是說最終決定調用方法是哪一個的,不僅僅是方法的接收者,還受參數類型的決定。
Visitor 模式
在GoF 的設計模式中,Visitor 模式就使用到了Double Dispatch 達到了調用真實對象的目的。
對於Visitor 模式,最常用的例子是樹的遍歷。比如在處理到節點和樹葉時的方式有區別,此歸通過visitor 的雙重分發,實現對於不同的Element ,執行不同的內容。
代碼類似這樣:
node.accept(new ConcreateVisitor()); leaf.accept(new ConcreateVisitor());
node 中的accept方法,會將自己的真實類型再次傳遞回visitor
public void accept(Visitor v) { v.visit(this); }此時,在visitor中,就能根據真實的類型來調用具體的方法,對應node 和leaf 分別有類似這樣的方法:
public void visit(Node n); public void visit(Leaf l);
Visitor 總結起來一般是包含visitor 接口,在visitor 接口中,包含各個即將被訪問的Element對象的處理邏輯。在各個Element 的具體實現中,再將自己的類型傳遞回visitor 進行二次分發,實現確切邏輯的調用。
在Tomcat中的應用
Visitor 在Tomcat中也有應用,典型的是解析EL表達式。
比如org.apache.el.parser.Node
這個類中包含一個accept(NodeVisitor visitor)方法
實際的Node 類型有很多,但在真實調用的這個時候,會通過
public void accept(NodeVisitor visitor) throws Exception { visitor.visit(this);將真實類型傳回visitor, vistor中會調用具體的方法,從而實現參數也能起到決定作用的功能。
public void visit(Node node) throws ELException { if (node instanceof AstFunction) { AstFunction funcNode = (AstFunction) node; Method m = null; } else if (xxx) { }這裡一般會聲明多個visit方法,然後上面的visit(this)會直接定位到目標方法上。
以上就是Java 中的各類分發,以及visitor這種模式通過模式的形式來實現雙重分發的效果。希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!