Visitor模式加深理解
靜態分派,動態分派,多分派,單分派 -------------- visitor模式準備
一,靜態分派:
1,定義:發生在編譯時期,分派根據靜態型別資訊發生,過載就是靜態分派
2,什麼是靜態型別:變數被宣告時的型別是靜態型別
什麼是動態型別:變數所引用的物件的真實型別
3,有兩個類,BlackCat ,WhiteCat都繼承自Cat
如下呼叫
class WhiteCat extends Cat{}
class BlackCat extends Cat{}
public class Person {
public void feed(Cat cat){
System.out.println("feed cat");
}
public void feed(WhiteCat cat){
System.out.println("feed WhiteCat");
}
public void feed(BlackCat cat){
System.out.println("feed BlackCat");
}
public static void main(String[] args) {
Cat wc = new WhiteCat();
Cat bc = new BlackCat();
Person p = new Person();
p.feed(wc);
p.feed(bc);
}
}
feed cat
feed cat
這樣的結果是因為過載是靜態分派,在編譯器執行的,取決於變數的宣告型別,因為wc ,bc都是Cat所以呼叫的都是feed(Cat cat)的函式.
二,動態分派
1,定義:發生在執行期,動態分派,動態的置換掉某個方法。
還是上邊類似的例子:
public void eat(){
System.out.println("cat eat");
}
}
public class BlackCat extends Cat{
public void eat(){
System.out.println("black cat eat");
}
public static void main(String[] args){
Cat cat = new BlackCat();
cat.eat();
}
}
這個時候的結果是:
black cat eat
這樣的結果是因為在執行期發生了向下轉型,就是動態分派了。
三,單分派:
1,定義:根據一個宗量的型別進行方法的選擇
四,多分派:
1,定義:根據多於一個宗量的型別對方法的選擇
2,說明:多分派其實是一系列的單分派組成的,區別的地方就是這些但分派不能分割。
3,C++ ,Java都是動態單分派,靜態多分派語言
多分派的語言有:CLOS Cecil
訪問差異型別的集合類--visitor模式入門
本文對應程式碼下載這裡
一,問題提出
訪問同一型別的集合類是我們最常見的事情了,我們工作中這樣的程式碼太常見了。
2 while (ie.hasNext()){
3 Person p = (Person)ie.next();
4 p.doWork();
5 }
這種訪問的特點是集合類中的物件是同一類物件Person,他們擁有功能的方法run,我們呼叫的恰好是這個共同的方法。
在大部份的情況下,這個是可以的,但在一些複雜的情況,如被訪問者的繼承結構複雜,被訪問者的並不是同一類物件,
也就是說不是繼承自同一個根類。方法名也並不相同。例如Java GUI中的事件就是一個例子。
例如這樣的問題,有如下類和方法:
類:PA ,方法:runPA();
類:PB ,方法:runPB();
類:PC ,方法:runPC();
類:PD ,方法:runPD();
類:PE ,方法:runPE();
有一個集合類List
List list = new ArrayList();
list.add(new PA());
list.add(new PB());
list.add(new PC());
list.add(new PD());
list.add(new PE());
....
二:解決
要求能訪問到每個類的對應的方法。我們第一反應應該是這樣的。
2 while (ie.hasNext()){
3 Object obj = ie.next();
4 if (obj instanceof PA) {
5 ((PA)obj).runPA();
6 }else if (obj instanceof PB) {
7 ((PB)obj).runPB();
8 }else if (obj instanceof PC) {
9 ((PC)obj).runPC();
10 }else if (obj instanceof PD) {
11 ((PD)obj).runPD();
12 }else if (obj instanceof PE) {
13 ((PE)obj).runPE();
14 }
15 }
三:新問題及分析解決
當數目變多的時候,維護if else是個費力氣的事情:
仔細分析if,else做的工作,首先判斷型別,然後根據型別執行相應的函式
如何才能解決這兩個問題呢?首先想到的是java的多型,多型就是根據引數執行相應的內容,
能很容易的解決第二個問題,我們可以寫這樣一個類:
2 public void run(PA pa) {
3 pa.runPA();
4 }
5 public void run(PB pb) {
6 pb.runPB();
7 }
8 public void run(PC pc) {
9 pc.runPC();
10 }
11 public void run(PD pd) {
12 pd.runPD();
13 }
14 public void run(PE pe) {
15 pe.runPE();
16 }
17 }
這樣只要呼叫run方法,傳入對應的引數就能執行了。
還有一個問題就是判斷型別。由於過載(overloading)是靜態多分配(java語言本身是支援"靜態多分配"的。
關於這個概念請看這裡)所以造成過載只根據傳入物件的定義型別,而不是實際的型別,所以必須在傳入前就確定型別,
這可是個難的問題,因為在容器中物件全是Object,出來後要是判斷是什麼型別必須用
if (xx instanceof xxx)這種方法,如果用這種方法啟不是又回到了原點,有沒有什麼更好的辦法呢?
我們知到Java還有另外一個特點,覆寫(overriding),而覆寫是"動態單分配"的(關於這個概念見這裡),
那如何利用這個來實現呢?看下邊這個方法:
我們讓上邊的一些類PA PB PC PD PE都實現一個介面P,加入一個方法,accept();
2 // 把自己傳入1
3 v.run( this );
4 }
5 然後在visitor中加入一個方法
6 public void run(P p){
7 // 把自己傳入2
8 p.accept( this );
9 }
10 // 這樣你在遍歷中可以這樣寫
11 Visitor v = new Visitor();
12 Iterator ie = list.iterator();
13 while (ie.hasNext()){
14 P p = (P)ie.next();
15 p.accept(v);
16 }
17 }
首先執行的是"把自己傳入2",在這裡由於Java的特性,實際執行的是子類的accept(),也就是實際類的accept
然後是"把自己傳入1",在這裡再次把this傳入,就明確型別,ok我們巧妙的利用overriding解決了這個問題
其實歸納一下第二部分,一個關鍵點是"自己認識自己",是不是很可笑。
其實在計算計技術領域的很多技術上看起來很高深的東西,其實就是現有社會中人的生活方式的一種對映
而且這種方式是簡單的不能再簡單的方式。上邊的全部過程基本上是一個簡單的visitor模式實現,visitor模式
已經是設計模式中比較複雜的模式了,但其實原理簡單到你想笑。看看下邊這個比喻也許你的理解會更深刻。
四:一個幫助理解的比喻:
題目:指揮工人工作
條件:你有10個全能工人,10樣相同工作。
需求:做完工作
實現:大喊一聲所有人去工作
條件變了,工人不是全能,但是工作相同,ok問題不大
條件再變,工作不是相同,但工人是全能,ok問題不大
以上三種情況在現實生活中是很少發生得,最多的情況是這樣:
10個工人,每人會做一種工作,10樣工作。你又一份名單Collection)寫著誰做什麼。但你不認識任何人
這個時候你怎麼指揮呢,方案一:
你可以一個個的叫工人,然後問他們名字,認識他們,查名單,告訴他們做什麼工作。
你可以直接叫出他們名字,告訴他們幹什麼,不需要知到他是誰。
看起來很簡單。但如果你要指揮10萬人呢 ?而且人員是流動的,每天的人不同,你每天拿到一張文件。
其實很簡單,最常用的做法是,你把這份名單貼在牆上,然後大喊一聲,所有人按照去看,按照自己的分配情況去做。
這裡利用的關鍵點是"所有工人自己認識自己",你不能苛求每個工人會做所有工作,不能苛求所有工作相同,但你
能要求所有工人都認識自己。
再想想我們開始的程式,每個工人對應著PA PB PC PD PE....
所有的工人都使工人P
每個工人會做的東西不一樣runPA runPB runPC
你有一份名單Visitor(過載)記錄著誰做什麼工作。
看完上邊這些,你是不是會產生如下的問題:
問題:為什麼不把這些方法的方法名做成一樣的,那就可以解決了。
例如,我們每個PA ,PB ,PC都加入一個run 方法,然後run內部再呼叫自己對應的runPx()方法。
答案:有些時候從不同的角度考慮,或者因為實現的複雜度早成很難統一方法名。
例如上邊指揮人工作的例子的例子,其實run方法就是大叫一聲去工作,因為每個工人只會做一種工作,所以能行
但我們不能要求所有人只能會做一種事情,這個要求很愚蠢。所以如果每個工人會幹兩種或者多種工作呢,
也就是我PA 有runPA() walkPA()等等方法, PB有runPB() climbPB()等等。。。
這個時候按照名單做事才是最好的辦法。
五:作者的話
所以說模式中很多複雜的東西,在現實中其實是很基本的東西,多多代入代出能幫助理解模式。
visitor模式概念------------------- visitor模式進一步
visitor模式理論及學術概念------------------- visitor模式進一步
一,訪問者模式的角色:
抽象訪問者:宣告一個或者多個訪問操作,形成所有的具體元素都要實現的介面
具體訪問者:實現抽象訪問者所宣告的介面
抽象節點:宣告一個接受操作,接受一個訪問者物件作為參量
具體節點:實現了抽象元素所規定的接受操作
結構物件:遍歷結構中的所有元素,類似List Set等
二,在什麼情況下應當使用訪問者模式
訪問者模式應該用在被訪問類結構比較穩定的時候,換言之系統很少出現增加新節點的
情況。因為訪問者模式對開-閉原則的支援並不好,訪問者模式允許在節點中加入方法,
是傾斜的開閉原則,類似抽象工廠。
三,訪問者模式的缺點:
1,增加節點困難
2,破壞了封裝
因為訪問者模式的缺點和複雜性,很多設計師反對使用訪問者模式。個人感覺應該在瞭解的
情況下考慮衡量選擇。
過載overloading和覆寫overriding哪個更早起作用-- visitor幫助篇
接受建議,改一下標題.例子不太恰當,我刪除了。換成了迴文中的例子。過載overloading和覆寫overriding哪個更早執行-- visitor幫助篇
一:問題提出
雖然我們經常寫程式用到過載和覆寫,但是很少會考慮他們的執行順序。下邊的內容就是關於,他們同時出現時
哪個先起作用:
二:問題分析
Java是"動態單分派靜態多分派語言",這個定義已經多次提起,如果你不瞭解這些概念,看這裡"visitor模式準備"
所以就註定了過載(靜態多分派)要早於覆寫(動態單分派),因為靜態分派是編繹期實現的,動態分派是執行期實現的。
三:驗證
相關文章
- 加深對 JavaScript This 的理解JavaScript
- Groovy探索 Visitor模式模式
- Visitor模式和Observer觀察者模式模式Server
- 看visitor模式的感受模式
- 設計模式、用Delphi描述-->Visitor模式 (轉)設計模式
- 透過重構來加深理解——DDD
- HttpRuntime的認識與加深理解HTTP
- C++設計模式 - 訪問器模式(Visitor)C++設計模式
- C#設計模式系列:訪問者模式(Visitor)C#設計模式
- 設計模式--訪問者模式Visitor(行為型)設計模式
- JAVA設計模式之 訪問者模式【Visitor Pattern】Java設計模式
- 九、GO 程式設計模式:K8S VISITOR 模式Go程式設計設計模式K8S
- 設計模式的征途—16.訪問者(Visitor)模式設計模式
- Java Tip: 用Reflection實現Visitor模式 (轉)Java模式
- 初始化引數OPEN_CURSORS的理解加深
- 完成C++不能做到的事 - Visitor模式C++模式
- SQL Server之旅(6):使用winHex利器加深理解資料頁SQLServer
- 加深C# 中字串前加@符號理解以及使用~~C#字串符號
- Sql Server之旅——第六站 使用winHex利器加深理解資料頁SQLServer
- 【設計模式】詳解訪問者(Visitor)模式-有多段程式碼出沒設計模式
- 如何優雅地從四個方面加深對深度學習的理解深度學習
- 訪問者(Visitor)
- Visitor Pattern Introduction (轉)
- 通過一段彙編,加深對暫存器ESP和EBP的理解
- Reactor模式理解React模式
- 理解策略模式模式
- 理解MVVM模式MVVM模式
- 理解設計模式設計模式
- 工廠模式理解模式
- Reactor模式理解(二)React模式
- Builder模式的理解!!UI模式
- 設計模式理解設計模式
- 我的一個visitor實作
- 用實驗方法加深理解Oracle的外連線(left/right/full)和內連線(inner)Oracle
- PHP單例模式理解PHP單例模式
- PHP 單例模式理解PHP單例模式
- 深入理解代理模式模式
- 如何理解單例模式?單例模式