国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

visitor模式概念——visitor模式進一步

2019-11-17 04:43:07
字體:
來源:轉載
供稿:網友
一,訪問者模式的角色:
抽象訪問者:聲明一個或者多個訪問操作,形成所有的具體元素都要實現的接口
具體訪問者:實現抽象訪問者所聲明的接口
抽象節點:聲明一個接受操作,接受一個訪問者對象作為參量
具體節點:實現了抽象元素所規定的接受操作
結構對象:遍歷結構中的所有元素,類似List Set等
二,在什么情況下應當使用訪問者模式
訪問者模式應該用在被訪問類結構比較穩定的時候,換言之系統很少出現增加新節點的
情況。因為訪問者模式對開-閉原則的支持并不好,訪問者模式答應在節點中加入方法,
是傾斜的開閉原則,類似抽象工廠。
三,訪問者模式的缺點:
1,增加節點困難
2,破壞了封裝
因為訪問者模式的缺點和復雜性,很多設計師反對使用訪問者模式。個人感覺應該在了解的
情況下考慮衡量選擇.

靜態分派,動態分派,多分派,單分派 -------------- visitor模式預備 
一,靜態分派:
1,定義:發生在編譯時期,分派根據靜態類型信息發生,重載就是靜態分派
2,什么是靜態類型:變量被聲明時的類型是靜態類型
      什么是動態類型:變量所引用的對象的真實類型
3,有兩個類,BlackCat ,WhiteCat都繼續自Cat
如下調用
class Cat{}
class WhiteCat extends Cat{}
class BlackCat extends Cat{}
public class Person {
    public void feed(Cat cat){
        System.out.    }
    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,定義:發生在運行期,動態分派,動態的置換掉某個方法。
還是上邊類似的例子:
class Cat{
    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模式入門
訪問差異類型的集合類--visitor模式入門
本文對應代碼下載這里
一,問題提出
訪問同一類型的集合類是我們最常見的事情了,我們工作中這樣的代碼太常見了。

1  Iterator ie  =  list.iterator();
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());
....

二:解決
要求能訪問到每個類的對應的方法。我們第一反應應該是這樣的。

 1  Iterator ie  =  list.iterator();
 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的多態,多態就是根據參數執行相應的內容,
能很輕易的解決第二個問題,我們可以寫這樣一個類:

 1   public   class  visitor  {
 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();

 1   public   void  accept(visitor v)  {
 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()等等。。。
這個時候按照名單做事才是最好的辦法。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 丹巴县| 泾源县| 田东县| 赣榆县| 石家庄市| 镇安县| 弥渡县| 沧州市| 宿州市| 大丰市| 西乌珠穆沁旗| 游戏| 云林县| 福海县| 金阳县| 深州市| 丹寨县| 鄂托克前旗| 满洲里市| 长沙县| 文化| 平舆县| 繁昌县| 上饶县| 临猗县| 宁津县| 崇仁县| 花垣县| 樟树市| 嘉荫县| 威宁| 上思县| 滨海县| 思茅市| 顺平县| 高台县| 舒城县| 原平市| 天等县| 龙岩市| 东丽区|