設(shè)計模式在UI系統(tǒng)開發(fā)中的應(yīng)用(導(dǎo)讀)

整理硬盤纠永,發(fā)現(xiàn)了2012年撰寫的內(nèi)部培訓(xùn)PPT教程。
當時主要從事UI方面的工作核蘸,并且負責公司內(nèi)部培訓(xùn)巍糯,因此為了培訓(xùn)相關(guān)開發(fā)人員,特意編寫了一個基于windows 的C++ UI庫(當時移動尚未如此之火熱客扎,雖然是win32,但是思想是一樣的鳞贷,可以用于任何系統(tǒng)),用于演示UI的核心概念以及面向?qū)ο?/strong>和設(shè)計模式相關(guān)的內(nèi)容虐唠。

面向?qū)ο蠛驮O(shè)計模式的作用是什么?

我個人認為就是: 通過面向?qū)ο螅O(shè)計模式等思想或手段形成一種機制:封裝不變的部分惰聂,將可變的部分以虛函數(shù)或事件回調(diào)等方式公開給調(diào)用方疆偿。

面向?qū)ο蠼坛谭譃?面向?qū)ο笮枨蠓治觯?uml基礎(chǔ)及用法,實現(xiàn)一個復(fù)雜控件三部分組成(建立在設(shè)計模式實現(xiàn)的類庫上,等設(shè)計模式好了后搓幌,再開源)

設(shè)計模式教程由六個部分程序組成

設(shè)計模式代碼庫.png

UIPLib.lib(核心庫)

UI的核心是什么?

我個人將其歸納為: "一個中心杆故,四個基本點"

一個中心: 以樹數(shù)據(jù)結(jié)構(gòu)(控件樹)為中心

四個基本點: 控件的布局、控件的事件分發(fā)及處理溉愁,控件的臟區(qū)局部刷新处铛、控件的渲染

控件的布局/事件/刷新/渲染都是建立在控件樹不同的遍歷基礎(chǔ)上的。

由此可知拐揭,UIPLib的功能就是實現(xiàn)上述的“一個中心撤蟆,四個基本點框架“功能。

之所以說是框架堂污,是因為它建立了一套機制家肯,但是實際使用需要從該機制對應(yīng)的各個基類進行繼承實現(xiàn)。具體我們會在后面看到盟猖。

將不變的部分封裝在類庫中讨衣,將可變部分聲明為虛方法,由繼承者來進行override式镐,從而達到天人合一之境反镇。

內(nèi)容2.png
 1. 容器用到的設(shè)計模式:適配器/迭代器/策略/裝飾
  • ArrayList.h 中實現(xiàn)了泛型的動態(tài)數(shù)組,類似std::vector或js中的Array對象

  • Iterator.h 中主要定義了迭代器模式和線性列表的遍歷方向策略(從左到右娘汞,從右到左)

迭代器.png
線性遍歷策略.png
  • Node.h 中定義了基類CNode以及CTypeNode 泛型類歹茶,所有子類,請繼承自CTypeNode類

  • NodeIterator.h中定義了樹節(jié)點線性迭代需要的相關(guān)內(nèi)容

樹節(jié)點遍歷.png
遍歷之間的關(guān)系1.png
遍歷之間的關(guān)系2.png
遍歷之間的關(guān)系3.png

之所以花這么多力氣來解釋樹的遍歷,是因為其重要性辆亏,樹結(jié)構(gòu)可以說是我用的最多风秤,最強大的數(shù)據(jù)結(jié)構(gòu)。一定要掌握他扮叨!

UIPText.exe程序中缤弦,進行了8個迭代器的測試:

nodeIteratorTest.png
迭代測試結(jié)果.png
2.  ControlSystem用到的設(shè)計模式:組合及模板方法
  • 在這個部分中,使用了結(jié)構(gòu)型設(shè)計模式-組合(其實樹結(jié)構(gòu)就是經(jīng)典的組合模式)以及模板方法的模式彻磁。

  • 通過ControlSystem系統(tǒng)碍沐,建立一個自定義控件的插件體系
    后面看到的UIPControl.lib就是具體控件的實現(xiàn),而UIPLib中的ControlSystem定義的是共性部分衷蜓。典型的插件方式累提。

  • 通過ControlSystem提供CControlBase基類,你可以繼承CControlBase并實現(xiàn)自己的控件磁浇,然后可以將一個控件組成一個插件lib斋陪,或者一系列控件組成一個插件lib,進行鏈接導(dǎo)入置吓,靈活性大增无虚,后面會了解如何使用的。

  • ControlManager是Control的根節(jié)點衍锚,也是控件布局/事件分發(fā)/局部刷新/渲染的起始節(jié)點S烟狻!

    3. 事件系統(tǒng)用到的設(shè)計模式: 觀察者
    
  • 使用開源的FastDelegate庫戴质,并在FastDelegate基礎(chǔ)上增加了多播功能(實際就是實現(xiàn)了C#的delegate功能)度宦,并支持觸發(fā)順序的調(diào)整(其實該實現(xiàn)源碼來自于Torque3D Engine)

  • 和C# 事件類似的系統(tǒng),對于自定義事件告匠,只需如下:

自定義Render事件.png

定義好三者戈抄,注冊事件處理函數(shù),可以在任意地方觸發(fā)事件后专,在任意地方呛凶,以全局函數(shù),成員方法或靜態(tài)方法等方式進行回調(diào)行贪,超級好用的類C#事件系統(tǒng)

  • 這里用到的觀察者模式和書上的經(jīng)典描述有差別漾稀,因此在UIPTest.exe中實現(xiàn)了一個經(jīng)典版的觀察者模式,可以調(diào)用看看結(jié)果建瘫。并且在教程中分析了Java awt中的事件監(jiān)聽方式崭捍。

  • 對C# delegate事件體系的擴展:Document Object Model (DOM) Level 3 Events Specification

關(guān)于該事件系統(tǒng)(也就是經(jīng)典的冒泡事件系統(tǒng)),我是非常喜歡的。因此在后來啰脚,我花了點時間殷蛇,研究webkit相關(guān)源碼实夹,從中剝離出干凈代碼移植到的代碼中。

其實DOM Event事件系統(tǒng)依賴如下幾個特點:

  1. 需要一個樹結(jié)構(gòu)----已經(jīng)具有控件樹了
  2. 需要事件監(jiān)聽優(yōu)先級----在FastDelegate基礎(chǔ)上實現(xiàn)了根據(jù)權(quán)重調(diào)整觸發(fā)的先后順序
  3. DOM Event事件支持多播觸發(fā)----在FastDelegate基礎(chǔ)上實現(xiàn)了多播功能
  4. 冒泡與和捕抓----需要自己實現(xiàn)粒梦,我們只要實現(xiàn)這個就可以了
    其實冒泡事件的使用是很有技巧的亮航,用的好的話,是很令人愉悅的體驗匀们。

關(guān)于冒泡事件缴淋,并沒有在本系列教程中,如有需要就自己擴展一下

4. 渲染系統(tǒng)用到的設(shè)計模式:工廠方法
渲染系統(tǒng).png
  • 面向接口編程泄朴,使用工廠方法返回接口
  • 實現(xiàn)了GDI 渲染器重抖,很容易擴展到其他渲染backend.
  • 將結(jié)果渲染到IRenderImage中,然后bitblt到顯存祖灰,通過這種方式(雙緩存)避免閃爍
  • 使用渲染到紋理(IRenderImage),有利于應(yīng)用于3DAPI中钟沛,例如opengl/directX。
5.臟區(qū)局部刷新用到的設(shè)計模式: 模板方法
  • 因為使用windows局扶,所以使用了windows自有的InvalidateRect API進行臟區(qū)標記恨统,用于觸發(fā)重繪
  • 如果使用各自系統(tǒng)的控件體系,基本都有自己的臟區(qū)標記函數(shù)三妈,例如android中的invalidate方法延欠,ios中的setNeedsDisplayInRect方法,這些API都可以指定一個rect,需要重繪的地方限制在該rect中沈跨。
  • 關(guān)于臟區(qū)的使用:
臟區(qū)檢查.png
  • CControlBase的Render方法規(guī)定了調(diào)用的流程:
render模板方法.png
  • 由此可見,子類override OnXXXRender時兔综,必須要先調(diào)用基類相同名稱的方法才OK饿凛。
    例如UIPControl.lib中自定義的控件的OnRender方法的override必須要要調(diào)用基類同名方法
例子.png
  • 關(guān)于臟區(qū)重繪,是個很好玩的領(lǐng)域软驰,是2D游戲或UI引擎性能提高的關(guān)鍵要素涧窒。在這個部分,我有過一定的研究锭亏,并且在opengl/directX上實現(xiàn)了一個效率極高的臟區(qū)刷新及渲染引擎纠吴。實際上,gl/dx這種圖形api慧瘤,通過修改投影矩陣戴已,我們可以在頂點處理階段就進行裁剪,將渲染效率大幅度提高锅减。糖儡、這部分涉及到gl/dx的渲染流水線以及數(shù)學相關(guān)知識。在完成本文檔后再深入的了解一些比較好玩的內(nèi)容怔匣。

  • 關(guān)于控件的布局握联,包括控件的尺寸和位置計算,是最復(fù)雜的一個部分,在我們現(xiàn)在的引擎中金闽,并沒有實現(xiàn)具體的布局引擎纯露,關(guān)于布局,在完成本文檔后再深入的了解布局相關(guān)內(nèi)容代芜。 說實話埠褪,布局算法太多了,這個需要深入思考后蜒犯,具體一一描述了组橄。每個算法背后都是有優(yōu)缺點。

    7.  UIPLib的入口類CApplication用到的設(shè)計模式:單例和模板方法
    
  • init模板方法罚随,初始化各個子系統(tǒng)

初始化模板方法.png
  • 鼠標和鍵盤事件的分發(fā)

  • RunLoop

  • CApplication本身是單例模式玉工,支持游戲模式(獨占模式,cpu 100%)和UI模式

UIPControl.lib庫(實現(xiàn)兩個在后面程序中要用到的控件)

內(nèi)容3.png
  • UIPIconButton 用來顯示帶Icon的Button淘菩,演示了如何增加事件以及渲染

  • UIPaintArea 與Canvas類似遵班,規(guī)定了一個顯示區(qū),并演示了CRenderEvent事件如何使用

paintArea.png

UIPAnimation.exe (以UIPLib/UIPControl為基礎(chǔ)潮改,實現(xiàn)一個貝塞爾路徑跟隨動畫演示效果)

生成路徑.gif
貝塞爾曲線路徑跟隨及方向調(diào)整.gif

用到的設(shè)計模式:

  • ISprite使用橋接模式實現(xiàn)
    //動畫精靈接口狭郑,橋接模式
    //橋接模式的核心是接口與實現(xiàn)分離
    //也就是面向接口編程

  • 工廠方法(使用std::shared_ptr防止內(nèi)存泄露)

精靈工廠.png
  • CPathFollower類使用flyWeight模式
flyweight.png
  • CAnimationController使用備忘錄模式,用于實現(xiàn)Record/Replay功能

UIPPaint.exe (以UIPLib/UIPControl為基礎(chǔ)汇在,實現(xiàn)一個簡單的繪圖程序)

UIPaint.png
paint.png
  • CDrawerVisitor類使用了Vistor模式翰萨,用于渲染輔助功能
  • 使用Command模式實現(xiàn)了Undo/Redo功能,在程序中你可以使用鼠標中鍵來進行Undo,使用鼠標右鍵來進行Redo.或者使用ctrl+z進行Undo糕殉,ctrl+y進行Redo.
  • ResponsibleChain系統(tǒng)使用職責鏈模式實現(xiàn)了一個幫助系統(tǒng)亩鬼,程序中你按F1,會根據(jù)當前的控件焦點顯示幫助內(nèi)容
  • 程序中各個圖標的切換隨之功能切換(例如按圓形就繪制圓圈),使用狀態(tài)機模式來管理,整個操作就非常清晰明了阿蝶,特別棒
  • CFacadeMediator類是整個核心結(jié)構(gòu)雳锋,即實現(xiàn)了門面模式,又實現(xiàn)了中間者模式羡洁,他是大內(nèi)總管玷过,所有的事情都需要到這個類來中轉(zhuǎn)。
狀態(tài)子系統(tǒng).png
圖元子系統(tǒng).png
undo_redo子系統(tǒng).png
事件.png
其他.png
事件處理.png

具體細節(jié)和源碼筑煮,這幾天會全部出來辛蚊,畢竟都是現(xiàn)成的PPT,我就偷懶直接截圖黏貼好了真仲。分如下三篇:

CoreLib篇

Control/Animation篇

Paint篇

2017/8/15日更新說明:

關(guān)于此系列 原本4篇發(fā)布的續(xù)集目前撤回關(guān)閉嚼隘。

因為:

  • 原本是win32 gdi實現(xiàn),目前改成c++ opengles實現(xiàn)(使用nanovg庫)袒餐,跨平臺飞蛹,可以運行在ios/android/linux/windows上
  • js作為上層語言進行調(diào)用
  • 目前實現(xiàn)的是C# delegate類似事件回調(diào)谤狡,在此基礎(chǔ)上再實現(xiàn)如下兩個事件系統(tǒng):
    冒泡事件系統(tǒng)Document Object Model (DOM) Level 3 Events Specification【已實現(xiàn)】
    職責鏈事件系統(tǒng)(ios UIResponser)

關(guān)于三大js引擎(ms chakra core/google v8/mozilla spidemonkey 技術(shù)選型過程)以及跨平臺渲染引擎(cairo/google skia/nanovg技術(shù)選型過程)記錄在如下幾篇周記中(還記錄了其他一些東西)。目前理論基礎(chǔ)已經(jīng)可行性驗證都已通過卧檐!

周記(2017/3/26-2017/4/2)

開發(fā)周記(2017/4/3-2017/4/9)

開發(fā)周記(2017/4/10-2017/4/16)

開發(fā)周記(2017/4/24-2017/5/1)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墓懂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子霉囚,更是在濱河造成了極大的恐慌捕仔,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盈罐,死亡現(xiàn)場離奇詭異榜跌,居然都是意外死亡,警方通過查閱死者的電腦和手機盅粪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門钓葫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人票顾,你說我怎么就攤上這事础浮。” “怎么了奠骄?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵豆同,是天一觀的道長。 經(jīng)常有香客問我含鳞,道長影锈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任蝉绷,我火速辦了婚禮迈勋,結(jié)果婚禮上亥啦,老公的妹妹穿的比我還像新娘嘿般。我一直安慰自己魂仍,他們只是感情好沃但,可當我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布磁滚。 她就那樣靜靜地躺著,像睡著了一般宵晚。 火紅的嫁衣襯著肌膚如雪垂攘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天淤刃,我揣著相機與錄音晒他,去河邊找鬼。 笑死逸贾,一個胖子當著我的面吹牛陨仅,可吹牛的內(nèi)容都是我干的津滞。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼灼伤,長吁一口氣:“原來是場噩夢啊……” “哼触徐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狐赡,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤撞鹉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后颖侄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸟雏,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年览祖,在試婚紗的時候發(fā)現(xiàn)自己被綠了孝鹊。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡穴墅,死狀恐怖惶室,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玄货,我是刑警寧澤皇钞,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站松捉,受9級特大地震影響夹界,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隘世,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一可柿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丙者,春花似錦复斥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纷捞,卻和暖如春痢虹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背主儡。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工奖唯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糜值。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓丰捷,卻偏偏與公主長得像坯墨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瓢阴,可洞房花燭夜當晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容