<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    love fish大鵬一曰同風起,扶搖直上九萬里

    常用鏈接

    統計

    積分與排名

    friends

    link

    最新評論

    訪問差異類型的集合類--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()等等。。。
    這個時候按照名單做事才是最好的辦法。

    五:作者的話
    所以說模式中很多復雜的東西,在現實中其實是很基本的東西,多多代入代出能幫助理解模式。

    看完本文,如果你對visitor模式有更多的興趣,想了解更多請看如下幾篇文章。
    1,靜態分派,動態分派,多分派,單分派 --------------?? visitor模式準備
    2,訪問差異類型的集合類 ------------------------?? visitor模式入門(本文)
    3,visitor模式理論及學術概念-------------------?? visitor模式深入
    4,重載overloading和覆寫overriding哪個更早執行--?? visitor幫助篇
    雖然排列順序是1,2,3,4 但是我個人建議的學習方式是2,1,3,4因為這個順序更方便一般人理解



    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-19 09:37 hannyu

    我有一個疑問,下面這段代碼是不是少寫了什么東西?我怎么覺得p.accept與v.run之間是死循環呢?請解釋一下
    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 } ??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-19 09:46 themax

    首先執行的是"把自己傳入2",???
    應該是"把自己傳入1"吧.

    ??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-19 21:53 dreamstone

    to hannyu :
    其實這就是visitor模式關鍵的地方,p.accept(v)的時候會發生向下轉型,所以執行的是子類的accept()方法,而子類的accept()方法中,傳入this就是子類而不是父類了。也就是說PA中調用v.run(this)其實傳入的this是PA類型,而不是P類型了,這樣visitor重載的時候就會執行run(Pa pa)而不是run(P p),所以不會死循環的。代碼在公司,明天把代碼上傳,你執行一下就知道了。
    (上邊就是說的自己認識自己了)??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-19 21:56 dreamstone

    to themax :
    可能我寫的有點歧意,我上邊的“把自己傳入2";是個label,相當于姓名一,姓名二,下邊解釋的是拿它來當label用,而不是把自己傳入給第二個,sorry。??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-19 22:31 dreamstone

    另外,加入了source的下載,見文章開始,雖然簡單,但是可以看看。
    個人感覺visitor模式是模式中比較復雜的。實現起來也是比較巧的.兩次this的利用。好像有一種學術的叫法"返還球"??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-20 09:22 hannyu

    多謝,看了源代碼終于明白了,visitor模式繞來繞去的真難理解。接口P應該只起來一個輔助作用,如果有個特殊點的命名就容易理解一些了。比如visitee???回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-27 13:07 jounyc

    我覺得把Visitor中

    public void run(Person p)
    {
    p.accept(this);
    }

    去掉也可以運行,
    因為在 p.accept(v); 時父類向下轉型,執行子類的accept(Visitor v),
    然后就執行子類在Visitor中對應的run()了,和上面那段沒有關系阿。

    樓主出來解釋下。??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門 2006-12-27 19:52 dreamstone[匿名]

    呵呵,其實我當時寫這個是為了這樣的情況,可能這種情況用的更多
    Visitor v = new Visitor();
    Iterator ie = list.iterator();
    while (ie.hasNext()) {
    P p = (P)ie.next();
    v.run(p);
    }
    這個時候那個函數就必須有了。因為run(p)的時候必須在編譯器有一個對應的函數,這兩個的區別是看想問題的角度了。
    是讓visitor 訪問每一個p
    還是讓每一個P運行。
    不過更多情況應該是v.run(p);更復合思考的習慣。后來發文的時候簡化了一下。


    ??回復??更多評論??

    # ?re: 訪問差異類型的集合類--visitor模式入門[未登錄]2007-02-09 11:52 xmlspy

    代碼中

    for(int i=0;i<list.size();i++){
    P p = (P)list.get(i);
    p.accept(v);
    }

    這種方式嚴重影響性能!!

    改成:

    P p =null;
    for(int i=0;i<list.size();i++){
    p = (P)list.get(i);
    p.accept(v);
    }
    ??回復??更多評論??

    posted on 2007-02-26 09:34 liaojiyong 閱讀(276) 評論(0)  編輯  收藏 所屬分類: Java

    主站蜘蛛池模板: 曰批免费视频播放免费 | 国产大片免费观看中文字幕| 亚洲小说区图片区| 99精品国产成人a∨免费看| 日韩亚洲AV无码一区二区不卡| 99久久免费观看| 亚洲女人影院想要爱| 无人影院手机版在线观看免费| 亚洲欧美成人一区二区三区| 狠狠久久永久免费观看| 边摸边吃奶边做爽免费视频网站| 亚洲AV永久无码精品一区二区国产| 无码免费又爽又高潮喷水的视频| 亚洲国产精品无码久久九九| 成人毛片100免费观看| 亚洲av无码国产精品色午夜字幕| 一个人免费日韩不卡视频| 亚洲成a人片毛片在线| 最近免费中文字幕大全| 美女视频黄.免费网址| 亚洲综合在线另类色区奇米| 久久久久国产精品免费看| 亚洲网站在线免费观看| 免费黄网在线观看| 一级特级女人18毛片免费视频| 亚洲熟女少妇一区二区| 四虎免费影院ww4164h| 亚洲AV噜噜一区二区三区 | 欧美a级在线现免费观看| 亚洲国产综合AV在线观看| 久久精品国产精品亚洲下载 | 很黄很色很刺激的视频免费| 怡红院亚洲红怡院在线观看| 国产精品国产亚洲精品看不卡| 国产va免费精品观看精品| 特级aa**毛片免费观看| 亚洲一区综合在线播放| 国产一区二区三区免费在线观看| 国产一区二区免费视频| 亚洲精品理论电影在线观看| 亚洲Av无码专区国产乱码DVD|