青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

在寫DirectUI時(shí)有這么一個(gè)需求,就是加載一串XML,如何生成一棵對(duì)應(yīng)的控件樹?

比如XML如下:

<panel>

       <button id=”1” text=”button”/>

<label id=”2” text=”label”/>

      <panel>

            <label id=”3” text=”good”/>

        </panel>

 

</panel>

如何生成對(duì)應(yīng)的Control Tree?

 

看到這個(gè),我們首先想到的是先對(duì)這個(gè)控件層次體系進(jìn)行設(shè)計(jì), 并抽象出如下層次:

Class CControlBase

{};

 

Class CButton: public CControlBase

{};

 

Class CLabel: public CControlBase

{};

 

Class CContainer: ControlBase

{};

 

Class CPanel: public CContainer

{};

 

我們可以看到他們的控件類層次如下:

以上層次符合一般面向?qū)ο笤O(shè)計(jì)的規(guī)則,其中CContainer表示控件容器類的基類。

那么如何根據(jù)XML創(chuàng)建這些對(duì)象呢?

我們想到了抽象工廠(Abstract Factory), 定義如下:

Class CControlFactory

{

Public:

       CButton* CreateButton() { return new CButton;}

       CLabel* CreateLabel() { return new CLabel;}

       CPanel* CreatePanel() { return new CPanel;}

}

 

顯然,以上設(shè)計(jì)每次加入一個(gè)新控件都要重新加一個(gè)Create方法,不符合面向?qū)ο笤O(shè)計(jì)的開放封閉原則(OCPOpen Closed Principle), 因此我們把它改成下面的接口:

Class CControlFactory

{

Public:

       CControlBase* CreateControl(const string& strTypeName)

{

              If(strTypeName==”button”)

return new Button;

              Else if(strTypeName==”label”)

return new Label;

              Else if(strTypeName==”panel”)

return new Panel;

              Else

                     Return null;

}

};

 

顯然上面的設(shè)計(jì)盡管比前一個(gè)好多了, 但是仍然有硬編碼(hard code)的味道, 每次新加一個(gè)控件,都要在這里改代碼。

 

如何才能新加控件,又不影響這里的代碼呢?

我們想到了注冊(cè)機(jī)制, 設(shè)計(jì)如下:

Class CControlFactory

{

Public:

       Bool RegisterControl(const string& strTypeName, void* lpfnCreateFun);

       CControlBase* CreateControl(const string& strTypeName) ;

 

Protected:

       map<string, void*> m_controlMap;

};

我們看到我們上面的設(shè)計(jì)是通過注冊(cè)控件和它的創(chuàng)建方法,保存在一個(gè)Map里,然后在CreateControl時(shí)通過查詢這個(gè)Map,調(diào)用控件注冊(cè)的創(chuàng)建方法, 來生成新控件。

可以看到上面的設(shè)計(jì)已經(jīng)完全符合開放封閉原則,我們新加控件完全不會(huì)影響現(xiàn)有的代碼。

 

但是考慮這樣一個(gè)需求,要求新生成控件要有默認(rèn)風(fēng)格,并且這些風(fēng)格是可變的,比如我們要生成的Button 默認(rèn)風(fēng)格是要求以某個(gè)圖片為背景。

 

顯然上面注冊(cè)創(chuàng)建函數(shù)的方法滿足不了我們這里的需求,因?yàn)槲覀儾豢赡芡ㄟ^在我們的創(chuàng)建函數(shù)里硬編碼來指定初始控件風(fēng)格。

 

怎么樣才能讓我們新創(chuàng)建的控件有默認(rèn)風(fēng)格,并且該默認(rèn)風(fēng)格是可變的?

我們想到了設(shè)計(jì)模式里創(chuàng)建型模式中的原型(Prototype)模式, 給我們的基本控件增加一個(gè)Clone方法, 代碼如下:

Class CControlBase

{

Public:

       Virtual CControlBase* Clone() { return new CControlBase(*this);}

};

 

Class CButton: public CControlBase

{

Public:

       Virtual CControlBase* Clone() { return new CButton (*this);}

};

 

Class CLabel: public CControlBase

{

Public:

       Virtual CControlBase* Clone() { return new CLabel (*this);}

};

 

Class CContainer: ControlBase

{

Public:

       Virtual CControlBase* Clone() { return new CContainer (*this);}

};

 

Class CPanel: public CContainer

{

Public:

       Virtual CControlBase* Clone() { return new CPanel (*this);}

};

 

而我們ControlFactory的設(shè)計(jì)如下:

Class CControlFactory

{

Public:

       Bool RegisterControl(const string& strTypeName, CControlBase* pPrototypeControl);

       CControlBase* CreateControl(const string& strTypeName) ;

 

Protected:

       map<string, CControlBase*> m_controlMap;

};

可以看到在注冊(cè)時(shí)我們把控件原型保存起來,然后在CreateControl時(shí)通過該控件原型的Clone方法,創(chuàng)建我們的新控件。這種設(shè)計(jì)下,我們只要在注冊(cè)時(shí)提供不同的原型控件,后面創(chuàng)建時(shí)就有不同的默認(rèn)風(fēng)格,非常方便。

 

至此,我們基本完成了ControlFactory的工作。

這里還可以優(yōu)化的是這里的ControlFactory我們可以把它定義成單例(Singleton:

Class CControlFactory

{

Public:

       Static CControlFactory* GetInstacne();

Public:

       Bool RegisterControl(const string& strTypeName, CControlBase* pPrototypeControl);

       CControlBase* CreateControl(const string& strTypeName) ;

      

Protected:

       map<string, CControlBase*> m_controlMap;

};

 

有了Control層次和Control Factory,我們接下來考慮如何解析XML來生成Control Tree?

一般的設(shè)計(jì)會(huì)寫一個(gè)如下的類:

Class CControlBuilder

{

       Public:

              CControlBase* BuildControlTree(const string& strXML);

};

上面的設(shè)計(jì)直接通過傳入XML,生成Control Tree,看起來很完美。

 

但是顯然這里有2件事情要做,一件是XML的解析,另一件是Control Tree的生成。我們把這2個(gè)東西柔在一個(gè)類里面,對(duì)我們以后維護(hù)很不方便。

 

比如說我們本來用TinyXML來解析XML的,但是后來發(fā)現(xiàn)它是基于DOM的,效率太低,想改用別的XML解析器,這時(shí)你就會(huì)發(fā)現(xiàn)修改CControlBuilder這個(gè)類是多么痛苦了。

 

顯然,我們比較好的設(shè)計(jì)是我們應(yīng)該把XML解析和Build Control Tree2個(gè)功能分離開來,這也符合面向?qū)ο笤O(shè)計(jì)時(shí)的單一職責(zé)原則(Single Responsibility Principle)

 

我們抽象出一個(gè)XML的解析接口:

 

Class IXMLParser

{

       Public:

              Virtual bool SetDoc(const cstring& strXML);

              Virtual string GetTagName();

       Virtual bool FindElem(const string& strName );

       Virtual bool FindChildElem(const string& strName );

       Virtual bool IntoElem();

       Virtual bool OutOfElem();

       Virtual string GetAttrib(const string& strName) const;

       Virtual string GetChildAttrib(const string& strName) const;

};

 

Class CTinyXMLParser: public IXMLParser

{

      

};

 

可以看到通過這種方式,我們把XML的解析過程抽象出來,我們本身不用關(guān)心解析器的類型(說明:上面XML解析的設(shè)計(jì)不一定合理,只是一個(gè)示例)

 

現(xiàn)在我們可以開始寫我們的Control Tree Builder了,

Class CControlBuilder:

{

       Public:

              CControlBase* BuildControlTree(const string& strXML)

              {

                     CTinyXMLParser parser;

                     parser.SetDoc(strXML);

                     CControlFactory* pFactory = CControlFactory::Instance();

                    

                     String strControl = parser.GetTagName();

                     CControlBase* pRoot = pFactory->CreateContorl(strControl);

                     ….

 

                     Return pRoot;

}

};

 

顯然上面的方式, 我們修改XML解析器的類型很不方便,我們可以通過工廠方法(Factory Method)來方便以后擴(kuò)展。

 

 

Class CControlBuilder

{

       Public:

              CControlBase* BuildControlTree(const string& strXML)

              {

                     Auto_ptr< IXMLParser > parser = CreateXMLParser() ;

                     Parser->SetDoc(strXML);

                     CControlFactory* pFactory = CControlFactory::Instance();

                    

                     String strControl = parser->GetTagName();

                     CControlBase* pRoot = pFactory->CreateContorl(strControl);

                     ….

 

                     Return pRoot;

}

 

Protected:

Virtual IXMLParser* CreateXMLParser()

{

       Return new CTinyXMLParser;

}

};

 

好了,到這里我們所有的設(shè)計(jì)都全部完成了,基本類圖如下:

PS, 在設(shè)計(jì)CControlBuilder時(shí),本來考慮是不是該用設(shè)計(jì)模式中的生成器(builder)模式, 但是Builder模式強(qiáng)調(diào)的是同樣的創(chuàng)建過程,生成不同的產(chǎn)品。顯然我們這里無論XML如何解析,最終只有Root Control這一種產(chǎn)品。 所以這里如果用這個(gè)模式的話,就有點(diǎn)過度設(shè)計(jì)了。

 

總結(jié)一下,我們上面用了哪些設(shè)計(jì)模式?
Composite, Abstract Factory, Factory Method, Singleton, Prototype.
你看出來了嗎?
你們有更好的設(shè)計(jì)思路嗎?

 說明: (1)上面的代碼都只是設(shè)計(jì)時(shí)的偽代碼,拿來編譯肯定過不了
        (2)考慮Windows的窗口類注冊(cè)和創(chuàng)建機(jī)制,會(huì)發(fā)現(xiàn)盡管WindowsAPIC語言方式,但是設(shè)計(jì)思想是類似的。
        (3) 對(duì)于DirectUI來說,上面控件類的這種層次設(shè)計(jì)其實(shí)很濫, 有興趣的話可以學(xué)下WPFJ

 

 

posted on 2012-06-10 17:31 Richard Wei 閱讀(4600) 評(píng)論(4)  編輯 收藏 引用 所屬分類: 設(shè)計(jì)模式

FeedBack:
# re: 生成DirectUI 控件樹的設(shè)計(jì)過程
2012-06-11 16:38 | 飯中淹
我覺得有幾個(gè)問題
1- UI依賴了XML。因?yàn)槟闳绻淖償?shù)據(jù)源,你需要改變UI內(nèi)部的東西。
我做的系統(tǒng)里,用了抽象的DOM數(shù)據(jù)NODE的概念。XML只是建立數(shù)據(jù)NODE的一個(gè)源而已。

2- UI的創(chuàng)建很多都用不到。比如prototype這種,如果你想讓你的UI從數(shù)據(jù)上創(chuàng)建,那么你就用一個(gè)從數(shù)據(jù)創(chuàng)建就好了。

我的系統(tǒng)里,UI控件的CREATE都是只有統(tǒng)一的一個(gè)參數(shù),IDataNode。這樣接口非常簡(jiǎn)單。

  回復(fù)  更多評(píng)論
  
# re: 生成DirectUI 控件樹的設(shè)計(jì)過程
2012-06-11 18:12 | Richard Wei
@飯中淹

沒太看明白你說的問題, 但是我想對(duì)于UI來說很多東西是類似的,
就一個(gè)UI(窗口)本身是一棵遞歸的樹,內(nèi)部有容器和控件,相當(dāng)于樹的分支(node)和樹葉(leaf),并且可以無限遞歸, 這個(gè)層次很符合Composite模式,也很符合用XML來描述。

所以現(xiàn)在比較流行的UI框架(比如WPF, WEB頁面)都分2層來描述,一層是標(biāo)記(markup). 比如WPF里的XAML, Web頁面里的HTML; 另外一層是代碼(code), 比如WPF里用C#, Web頁面里用javascript.

其實(shí)本質(zhì)上code部分才是真正的實(shí)現(xiàn),內(nèi)部定義了所有的控件層次, 所以即使不依賴markup部分,我們同樣可以定義和操作所有的UI, 但是借助Markup可以讓我們更方便的生成和修改UI。

至于用不用Prototype模式不太重要,我這里的想法是讓控件有默認(rèn)的風(fēng)格屬性,而且這個(gè)默認(rèn)風(fēng)格屬性也是容易修改的。通過用Prototype,我可以在XML里定義默認(rèn)的控件原型,程序初始化時(shí)用該原型來注冊(cè)控件就可以了。另外, 如果有些控件你覺得用不到,不去注冊(cè)就好了。 這種注冊(cè)機(jī)制, 對(duì)于支持插件(控件)也很方便,你覺得呢?  回復(fù)  更多評(píng)論
  
# re: 生成DirectUI 控件樹的設(shè)計(jì)過程
2012-06-12 14:09 | 飯中淹
。。。。并不是說用XML不好,或者否定UI樹什么的。

我也是用的MARKUP來建立UI控件樹,并綁定代碼。不過,我是用了一個(gè)中間的DOM抽象數(shù)據(jù)層。
這樣,不管外面有什么XML,HTML甚至INI,我只要一個(gè)數(shù)據(jù)抽象層放進(jìn)UI里面就好了。





@Richard Wei
  回復(fù)  更多評(píng)論
  
# re: 生成DirectUI 控件樹的設(shè)計(jì)過程
2012-06-26 15:46 | 叫我老王吧
最近也在寫UI,受益匪淺,受教了  回復(fù)  更多評(píng)論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一本色道久久综合狠狠躁篇的优点| 欧美视频一区二| 亚洲精品欧美极品| 欧美激情亚洲视频| 欧美激情亚洲视频| 在线一区欧美| 亚洲嫩草精品久久| 韩国在线一区| 男女视频一区二区| 欧美精品精品一区| 亚洲欧美一级二级三级| 亚洲欧美日韩精品在线| 国产伊人精品| 亚洲成人资源| 你懂的视频一区二区| 一区二区三区福利| 亚洲一区二区三区四区在线观看| 国产精品永久免费在线| 久久一区二区三区国产精品| 免费观看欧美在线视频的网站| 一区二区三欧美| 午夜久久影院| 亚洲激情综合| 亚洲午夜电影网| 精品不卡一区二区三区| 亚洲激情视频在线播放| 国产精品美女久久久免费 | 在线一区视频| 国模精品一区二区三区色天香| 欧美大片va欧美在线播放| 欧美日韩精品在线| 久久青草久久| 欧美日韩国产在线| 久久久人成影片一区二区三区| 久久一区二区三区超碰国产精品| 在线视频你懂得一区二区三区| 亚洲欧美日韩天堂| 亚洲国产片色| 亚洲影院一区| 亚洲青涩在线| 亚洲欧美日韩第一区| 亚洲国产精品999| 亚洲色诱最新| 亚洲激情电影中文字幕| 亚洲私人影院在线观看| 红桃视频国产一区| 最近中文字幕日韩精品| 国产日韩欧美不卡| 亚洲日韩欧美视频| 好吊日精品视频| 在线性视频日韩欧美| 亚洲国产精品va| 亚洲一区二区三区四区五区午夜| 亚洲精品欧美专区| 欧美一区二区三区在线免费观看| 99视频精品| 久久久免费精品视频| 亚洲欧美国产高清| 欧美激情在线免费观看| 久久久欧美精品| 国产精品国产亚洲精品看不卡15| 亚洲第一福利视频| 国产一区二区三区无遮挡| 9久re热视频在线精品| 亚洲国产天堂网精品网站| 午夜精品一区二区三区在线播放| 一本色道久久加勒比精品| 久久婷婷av| 欧美有码视频| 欧美日韩影院| 亚洲国产片色| 在线看成人片| 欧美一区日韩一区| 午夜老司机精品| 欧美日本一道本在线视频| 女同性一区二区三区人了人一| 国产精品一级| 一本一本久久a久久精品综合麻豆 一本一本久久a久久精品牛牛影视 | 今天的高清视频免费播放成人| 中文亚洲免费| 中文在线资源观看视频网站免费不卡| 久久色在线播放| 久久久久久穴| 国产免费一区二区三区香蕉精| 一区二区福利| 一本久久a久久精品亚洲| 美女脱光内衣内裤视频久久影院| 久久蜜桃资源一区二区老牛 | 国内成+人亚洲| 亚洲一区免费视频| 亚洲午夜精品福利| 欧美日本韩国一区| 亚洲国产美女精品久久久久∴| 在线看片第一页欧美| 久久国产一区二区三区| 亚洲一区二区在线免费观看| 欧美日韩精品免费观看视频完整| 亚洲国产精品专区久久| 91久久线看在观草草青青| 久久嫩草精品久久久久| 久久视频国产精品免费视频在线| 国产日韩欧美亚洲一区| 亚洲欧美日韩在线综合| 午夜国产一区| 欧美日韩高清一区| 日韩视频二区| 一本不卡影院| 欧美日韩国产va另类| 亚洲免费av电影| 一区二区三区高清在线| 欧美日韩一卡二卡| 一区二区三区高清在线| 亚洲性夜色噜噜噜7777| 欧美午夜不卡视频| 亚洲一区二区三区乱码aⅴ| 亚洲一区二区三区欧美 | 国产欧美综合在线| 先锋影音久久| 久久综合成人精品亚洲另类欧美| 激情成人综合| 久热爱精品视频线路一| 亚洲二区在线| 夜夜嗨av一区二区三区免费区| 欧美乱大交xxxxx| 99国产精品久久久久久久| 一区二区三区波多野结衣在线观看| 欧美日韩国产在线播放网站| 一区二区三区四区五区在线| 午夜久久久久久| 国产亚洲欧美色| 久久躁日日躁aaaaxxxx| 亚洲国产三级网| 亚洲日本国产| 欧美国产日产韩国视频| 亚洲最新中文字幕| 欧美在线观看视频在线| 又紧又大又爽精品一区二区| 蜜臀av一级做a爰片久久| 亚洲青涩在线| 小黄鸭视频精品导航| 国产一区二区三区精品欧美日韩一区二区三区 | 午夜精品福利一区二区三区av| 久久久久99精品国产片| 亚洲国产精品成人| 欧美日韩精品一本二本三本| 亚洲一区三区电影在线观看| 久久免费国产精品| 亚洲韩国青草视频| 欧美调教vk| 欧美专区中文字幕| 亚洲国产精品专区久久| 亚洲欧美网站| 亚洲国产精品电影| 欧美日韩精品欧美日韩精品| 午夜日本精品| 亚洲电影第1页| 亚洲女优在线| 一区二区三区亚洲| 欧美日韩亚洲国产一区| 欧美一级艳片视频免费观看| 亚洲成色最大综合在线| 亚洲男人的天堂在线观看| 黄色精品免费| 欧美日韩一区二区三区在线| 欧美在线一区二区| 亚洲精品日产精品乱码不卡| 久久精品女人的天堂av| 亚洲精品欧洲精品| 国产亚洲视频在线观看| 欧美精品黄色| 久久国产日韩| 亚洲日本免费| 久久久噜噜噜久久人人看| 99riav久久精品riav| 国产一区二区三区高清| 欧美精品在线视频观看| 欧美一区网站| 亚洲精品久久久久久一区二区| 久久精品五月| 亚洲网站视频| 亚洲成人在线网站| 国产精品一二三四| 欧美日韩国产综合一区二区| 久久精品中文字幕一区二区三区| 99pao成人国产永久免费视频| 蜜桃av噜噜一区| 香蕉久久久久久久av网站| 亚洲裸体视频| 黄色成人片子| 国产精品porn| 欧美高清你懂得| 久久久久久久999精品视频| 一区二区三区av| 欧美韩日一区二区| 久久精品国产免费看久久精品| 亚洲图片欧洲图片av| 亚洲国产精品t66y| 国产亚洲一区精品| 国产精品国产| 欧美日本精品在线|