<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          經(jīng)典永不過(guò)時(shí)!重溫設(shè)計(jì)模式

          共 7719字,需瀏覽 16分鐘

           ·

          2021-04-11 22:15



          | 導(dǎo)語(yǔ) 在軟工程中,設(shè)計(jì)模式(design pattern)是對(duì)軟件設(shè)計(jì)中普遍存在(反復(fù)出現(xiàn))的各種問(wèn)題,所提出的解決方案。這個(gè)術(shù)語(yǔ)是由埃里?!べが敚‥rich Gamma)等人在1990年代從建筑設(shè)計(jì)領(lǐng)域引入到計(jì)算機(jī)科學(xué)的,設(shè)計(jì)模式是針對(duì)軟件設(shè)計(jì)中常見(jiàn)問(wèn)題的工具箱,其中的工具就是各種經(jīng)過(guò)實(shí)踐驗(yàn)證的解決方案。即使你從未遇到過(guò)這些問(wèn)題,了解模式仍然非常有用,因?yàn)樗苤笇?dǎo)你如何使用面向?qū)ο蟮脑O(shè)計(jì)原則來(lái)解決各種問(wèn)題。


          今天談一談設(shè)計(jì)模式,一名優(yōu)秀的開(kāi)發(fā),應(yīng)該多少都需要了解一些常用的設(shè)計(jì)模式和使用場(chǎng)景,讓我們一起來(lái)重溫一下那些年經(jīng)典設(shè)計(jì)模式;


          本文主要內(nèi)容



          為什么要掌握設(shè)計(jì)模式


          歷史的教訓(xùn)


          時(shí)間回到 20 世紀(jì) 80 年代,當(dāng)時(shí)的軟件行業(yè)正處于第二次軟件危機(jī)中。根本原因是,隨著軟件規(guī)模和復(fù)雜度的快速增長(zhǎng),如何高效高質(zhì)的構(gòu)建和維護(hù)這樣大規(guī)模的軟件成為了一大難題。無(wú)論是開(kāi)發(fā)何種軟件產(chǎn)品,成本和時(shí)間都最重要的兩個(gè)維度。較短的開(kāi)發(fā)時(shí)間意味著可比競(jìng)爭(zhēng)對(duì)手更早進(jìn)入市場(chǎng);較低的開(kāi)發(fā)成本意味著能夠留出更多營(yíng)銷資金,因此能更廣泛地覆蓋潛在客戶。


          設(shè)計(jì)模式是銀彈嗎?


          代碼復(fù)用是減少開(kāi)發(fā)成本,減低復(fù)雜度最常用的方式之一,這個(gè)想法表面看起來(lái)很棒,但實(shí)際上要讓已有代碼在全新的上下文中工作,通常還是需要付出額外努力的。組件間緊密的耦合、對(duì)具體類而非接口的依賴和硬編碼的行為都會(huì)降低代碼的靈活性,使得復(fù)用這些代碼變得更加困難。設(shè)計(jì)模式目標(biāo)就是幫助軟件提高內(nèi)聚,減低耦?????????????????合,?????????????????使用設(shè)計(jì)模式是增加軟件組件靈活性并使其易于復(fù)用的方式之一。


          變化是程序員生命中唯一不變的事情,客戶需求可能經(jīng)常會(huì)變,緊急上線的版本,要不要下次重構(gòu)一下,還是繼續(xù)打各種補(bǔ)丁, 技術(shù)債會(huì)越積越多,因此在設(shè)計(jì)程序架構(gòu)時(shí),所有有經(jīng)驗(yàn)的開(kāi)發(fā)者會(huì)盡量選擇支持未來(lái)任何可能變更的方式。可擴(kuò)展性成為了程序設(shè)計(jì)必須要考慮指標(biāo),而設(shè)計(jì)模式是可以借鑒的,成熟的優(yōu)化程序設(shè)計(jì)的解決方案;


          總體來(lái)說(shuō),深刻理解設(shè)計(jì)模式會(huì)給我們帶來(lái)很多好處:


          1. 可以和面試官"暢談"設(shè)計(jì)模式相關(guān)問(wèn)題.

          2. 很多開(kāi)源軟件框架大量使用了設(shè)計(jì)模式,比如Linux系統(tǒng),Redis,Spring,C++STL等等,可以把幫你加快理解開(kāi)源軟件框架。

          3. 當(dāng)你寫(xiě)的代碼越來(lái)越優(yōu)美后,你的代碼鑒賞能力就會(huì)提高,對(duì)團(tuán)隊(duì)code review貢獻(xiàn)也會(huì)更大,在個(gè)人影響力也會(huì)提高。

          4. 你不會(huì)再畏手畏腳,你的工具箱里面工具很多后,可以幫助你,應(yīng)對(duì)各種大型項(xiàng)目的代碼設(shè)計(jì)和開(kāi)發(fā)。

          5. 每個(gè)領(lǐng)域都會(huì)一些成熟"套路", 編程也不例外,熟悉這些套路,可以更好方便交流和更快速地解決問(wèn)題;


          為了更好理解設(shè)計(jì)模式,我們首先要理解一些重要的設(shè)計(jì)原則,而不是片面理解設(shè)計(jì)模式哪些模式名詞,要看清楚這背后的原理,這個(gè)才是最重要的。


          代碼設(shè)計(jì)原則


          代碼設(shè)計(jì)原則貫穿在整個(gè)設(shè)計(jì)模式之中,是理解其中的精華,本文討論了一些重要的設(shè)計(jì)原則,包括通用設(shè)計(jì)原則,DRY原則,KISS原則SOLID原則等:


          通用設(shè)計(jì)原則


          隔離變化

          找到程序中的變化內(nèi)容并將其與不變的內(nèi)容區(qū)分開(kāi),該原則的主要目的是將變更造成的影響最小化。


          面向接口編程

          面向接口進(jìn)行開(kāi)發(fā), 而不是面向?qū)崿F(xiàn);依賴于抽象類型,而不是具體類,要求接口標(biāo)準(zhǔn)化設(shè)計(jì),只要對(duì)外的接口沒(méi)有變,內(nèi)部實(shí)現(xiàn)就可以任意變化,為以后留有更多優(yōu)化空間,方便以后更新迭代,可以說(shuō)這樣的設(shè)計(jì)是靈活的。


          組合優(yōu)于繼承

          繼承可能是類之間最明顯、最簡(jiǎn)便的代碼復(fù)用方式。如果你有兩個(gè)代碼相同的類, 就可以為它們創(chuàng)建一個(gè)通用的基類,然后將相似的代碼移動(dòng)到其中。但繼承可能帶來(lái)的問(wèn)題:

          • 子類不能減少超類的接口。你必須實(shí)現(xiàn)父類中所有的抽象方法,即使它們沒(méi)什么用。

          • 在重寫(xiě)方法時(shí),你需要確保新行為與其基類中的版本兼容。這一點(diǎn)很重要,因?yàn)樽宇惖乃袑?duì)象都可能被傳遞給以超類對(duì)象為參數(shù)的任何代碼,相信你不會(huì)希望這些代碼崩潰的。

          • 繼承打破了超類的封裝,因?yàn)樽宇悡碛性L問(wèn)父類內(nèi)部詳細(xì)內(nèi)容的權(quán)限。此外還可能會(huì)有相反的情況出現(xiàn),那就是程序員為了進(jìn)一步擴(kuò)展的方便而讓超類知曉子類的內(nèi)部詳細(xì)內(nèi)容。

          • 子類與超類緊密耦合。超類中的任何修改都可能會(huì)破壞子類的功能。

          • 通過(guò)繼承復(fù)用代碼可能導(dǎo)致平行繼承體系的產(chǎn)生。繼承通常僅發(fā)生在一個(gè)維度中。只要出現(xiàn)了兩個(gè)以上的維度,你就必須創(chuàng)建數(shù)量巨大的類組合,從而使類層次結(jié)構(gòu)膨脹到不可思議的程度。


          組合是代替繼承的一種方法。繼承代表類之間的“是”關(guān)系(汽車是交通工具),而組合則代表“有”關(guān)系(汽車有一個(gè)引擎)。


          DRY 原則


          DRY-Don't Repeat Yourself(不要重復(fù)代碼)

          降低可管理單元的復(fù)雜度的基本策略是將系統(tǒng)分成多個(gè)部分。


          理解這一原理是如此重要,它通常以首字母縮寫(xiě)詞DRY來(lái)指代,并出現(xiàn)在Andy Hunt和Dave Thomas的書(shū)《實(shí)用程序員》中,但是這個(gè)概念本身已經(jīng)有很長(zhǎng)時(shí)間了。它指的是軟件的最小部分。


          當(dāng)您構(gòu)建一個(gè)大型軟件項(xiàng)目時(shí),通常會(huì)因整體復(fù)雜性而感到不知所措。人類不善于管理復(fù)雜性;他們擅長(zhǎng)為特定范圍的問(wèn)題找到有創(chuàng)意的解決方案。降低可管理單元的復(fù)雜性的基本策略是將系統(tǒng)分成更方便的部分。首先,您可能希望將系統(tǒng)分為多個(gè)組件,其中每個(gè)組件代表其自己的子系統(tǒng),其中包含完成特定功能所需的一切。


          KISS 原則



          KISS是使它保持簡(jiǎn)單,愚蠢的首字母縮寫(xiě),是美國(guó)海軍在1960年提出的設(shè)計(jì)原則。KISS原則指出,大多數(shù)系統(tǒng)如果保持簡(jiǎn)單而不是變得復(fù)雜,則效果最佳。因此,簡(jiǎn)單性應(yīng)該是設(shè)計(jì)的主要目標(biāo),并且應(yīng)該避免不必要的復(fù)雜性。



          SOLID 原則


          SOLID 原則是在羅伯特·馬丁的著作《敏捷軟件開(kāi)發(fā):原則、模式與實(shí)踐》中首次提出的,SOLID 是讓軟件設(shè)計(jì)更易于理解、更加靈活和更易于維護(hù)的五個(gè)原則的簡(jiǎn)稱。



          盡量讓每個(gè)類或者函數(shù)只負(fù)責(zé)軟件中的一個(gè)功能,這條原則的主要目的是減少?gòu)?fù)雜度,你不需要費(fèi)盡心機(jī)地去構(gòu)思如何僅用200 行代碼來(lái)實(shí)現(xiàn)復(fù)雜設(shè)計(jì),實(shí)際上完全可以使用十幾個(gè)清晰的方法,這里核心是: 通過(guò)實(shí)現(xiàn)最基本"原子函數(shù)", 其他復(fù)雜功能都可以通過(guò)這些原子函數(shù)構(gòu)建,每一層的函數(shù)語(yǔ)義都是單一的,通過(guò)層層封裝,最終構(gòu)建一個(gè)龐大可控的系統(tǒng)。


           

          本原則的主要理念是在實(shí)現(xiàn)新功能時(shí)能保持已有代碼不變,為什么呢,主要是修改存量代碼,很可能會(huì)影響軟件穩(wěn)定性,很多線上代碼跑了好多年了,經(jīng)歷很多輪迭代,各種補(bǔ)丁,如果考慮不全面,很容易帶來(lái)風(fēng)險(xiǎn),下圖比較形象說(shuō)明:




          替換原則是用于預(yù)測(cè)子類是否與代碼兼容,以及是否能與其超類對(duì)象協(xié)作的一組檢查。這一概念在開(kāi)發(fā)程序庫(kù)和框架時(shí)非常重要, 因?yàn)槠渲械念悓?huì)在他人的代碼中使用——你是無(wú)法直接訪問(wèn)和修改這些代碼的。里氏替換原則的重點(diǎn)在不影響原功能



          根據(jù)接口隔離原則,你必須將“臃腫”的方法拆分為多個(gè)顆粒度更小的具體方法??蛻舳吮仨殐H實(shí)現(xiàn)其實(shí)際需要的方法。否則,對(duì)于“臃腫”接口的修改可能會(huì)導(dǎo)致程序出錯(cuò),即使客戶端根本沒(méi)有使用修改后的方法。



          通常在設(shè)計(jì)軟件時(shí),你可以辨別出不同層次的類。

          ? 低層次的類實(shí)現(xiàn)基礎(chǔ)操作(例如磁盤(pán)操作、傳輸網(wǎng)絡(luò)數(shù)據(jù)和連接數(shù)據(jù)庫(kù)等)。

          ? 高層次類包含復(fù)雜業(yè)務(wù)邏輯以指導(dǎo)低層次類執(zhí)行特定操作。


          經(jīng)典設(shè)計(jì)模式


          這里列舉了22種設(shè)計(jì)模式,大致分為三類:創(chuàng)建型模式結(jié)構(gòu)型模式,行為模式


          創(chuàng)建型模式提供創(chuàng)建對(duì)象的機(jī)制,增加已有代碼的靈活性和可復(fù)用性



          結(jié)構(gòu)型模式介紹如何將對(duì)象和類組裝成較大的結(jié)構(gòu),并同時(shí)保持結(jié)構(gòu)的靈活和高效:



          行為模式負(fù)責(zé)對(duì)象間的高效溝通和職責(zé)委派:



          推薦一個(gè)經(jīng)典學(xué)習(xí)網(wǎng)站

          https://refactoring.guru


          上面每種模式配有形象圖,比如工廠方法模式:


           

          還提供對(duì)應(yīng)的設(shè)計(jì)類圖:



          也提供了對(duì)應(yīng)代碼示例



          支持9種語(yǔ)言的實(shí)現(xiàn):

              代碼在:https://github.com/RefactoringGuru


          推薦給大家,拿走別謝;


          更多請(qǐng)參考:

          《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》

            https://refactoring.guru



          Linux經(jīng)典設(shè)計(jì)模式


           內(nèi)核面向?qū)ο笤O(shè)計(jì)模式


          Linux雖然是面向過(guò)程的c語(yǔ)言寫(xiě)成的,但是卻可以表達(dá)面向?qū)ο蟮乃枷耄?span style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, Cambria, Cochin, Georgia, Times, serif;text-align: start;caret-color: rgb(0, 0, 0);">Linux內(nèi)核大量使用面向?qū)ο蟮木幋a風(fēng)格,我們可以從中至少學(xué)習(xí)到兩點(diǎn):

          • 說(shuō)明在大型軟件開(kāi)發(fā)中,OOP編程思想很重要,和具體語(yǔ)言無(wú)關(guān);

          • 同時(shí)展示了怎么用c語(yǔ)言實(shí)現(xiàn)OOP編程,值得廣大C語(yǔ)言開(kāi)發(fā)者學(xué)習(xí)。

          我們用例子來(lái)說(shuō)明。


          封裝

          以內(nèi)核proto定義為例:


          struct proto 定義傳輸層接口方法和相應(yīng)成員數(shù)據(jù),類似C++的class定義;可以根據(jù)這個(gè)class生產(chǎn)很多實(shí)例,比如TCP實(shí)例,可以通過(guò)統(tǒng)一接口訪問(wèn)TCP實(shí)例的方法和數(shù)據(jù)。


          繼承

          以內(nèi)核套接字體系為例:



          基于此繼承體系,對(duì)于一些接受 struct sock* 形參的接口,就可以直接把上述的子類套接字實(shí)例 struct udp_sock* sk作為實(shí)參傳進(jìn)去(當(dāng)然,這里需要指針強(qiáng)轉(zhuǎn)一次(struct sock*)sk)。這里就是OOP中“is a"的public繼承關(guān)系,子類對(duì)象可以直接作為父類對(duì)象使用,并且這種實(shí)現(xiàn)只支持單繼承。


          多態(tài)


          用C實(shí)現(xiàn)多態(tài)需要自己維護(hù)繼承關(guān)系中的虛函數(shù)體系,C++有編譯器自動(dòng)生成、維護(hù)vtbl與vptr。Linux內(nèi)核的實(shí)現(xiàn)中,將系列函數(shù)指針?lè)湃虢Y(jié)構(gòu)體,即視其為“虛函數(shù)”,亦或是專門定義一個(gè)xxx_ops結(jié)構(gòu),里面放上一堆函數(shù)指針,作為“虛函數(shù)表”。仍以套接字體系為例,在基類 sock 中,有協(xié)議結(jié)構(gòu)體指針 struct proto *skc_prot; 這個(gè)proto即可大體上視為一個(gè)虛函數(shù)表vtbl,內(nèi)有具體協(xié)議的函數(shù)指針,而這個(gè)skc_prot指針,即可視為虛指針vptr。



          在套接字創(chuàng)建時(shí),根據(jù)參數(shù)中的協(xié)議族、協(xié)議類型、協(xié)議號(hào)信息,調(diào)用協(xié)議族的create函數(shù)執(zhí)行創(chuàng)建,綁定具體協(xié)議proto指針到該vptr上,自此實(shí)現(xiàn)了靜態(tài)類型到動(dòng)態(tài)類型的綁定。之后,當(dāng)調(diào)用虛函數(shù)時(shí),即可直接通過(guò)這些函數(shù)指針進(jìn)行多態(tài)的調(diào)用 , 比如下面例子socket調(diào)用connect接口:


          這里第一個(gè)參數(shù)sk即可看做this指針,不同socket對(duì)象,會(huì)訪問(wèn)對(duì)應(yīng)協(xié)議接口,從而實(shí)現(xiàn)多態(tài)訪問(wèn):



          list 設(shè)計(jì)模式


          list作為常用數(shù)據(jù)結(jié)構(gòu),寫(xiě)代碼時(shí)候經(jīng)常會(huì)遇到,可以看一下傳統(tǒng)list設(shè)計(jì)和內(nèi)核list設(shè)計(jì)有什么不一樣。


          一般的雙向鏈表一般是如下的結(jié)構(gòu):

          • 有個(gè)單獨(dú)的頭結(jié)點(diǎn)(head)

          • 每個(gè)節(jié)點(diǎn)(node)除了包含必要的數(shù)據(jù)之外,還有2個(gè)指針(pre,next)

          • pre指針指向前一個(gè)節(jié)點(diǎn)(node),next指針指向后一個(gè)節(jié)點(diǎn)(node)

          • 頭結(jié)點(diǎn)(head)的pre指針指向鏈表的最后一個(gè)節(jié)點(diǎn)

          • 最后一個(gè)節(jié)點(diǎn)的next指針指向頭結(jié)點(diǎn)(head)



          傳統(tǒng)list如下圖:


          傳統(tǒng)的鏈表不同node類型,需要重新定義結(jié)構(gòu),不夠通用化,還需要為node實(shí)現(xiàn)脫鏈、入鏈操作等。


          我們需要抽象出一個(gè)“基類”來(lái)實(shí)現(xiàn)鏈表的功能,其他數(shù)據(jù)結(jié)構(gòu)只需要簡(jiǎn)單的繼承這個(gè)鏈表類就可以了。


          內(nèi)核list設(shè)計(jì)如下:


          • 鏈表不是將用戶數(shù)據(jù)保存在鏈表節(jié)點(diǎn)中,而是將鏈表節(jié)點(diǎn)保存在用戶數(shù)據(jù)中

          • 鏈表節(jié)點(diǎn)只有2個(gè)指針(prev和next)

          • prev指針指向前一個(gè)節(jié)點(diǎn)的鏈表節(jié)點(diǎn),next指針指向后一個(gè)節(jié)點(diǎn)(node)的鏈表節(jié)點(diǎn)

          ??????????????????????????????



          如下圖:



          這樣設(shè)計(jì)的好處是鏈表的節(jié)點(diǎn)將獨(dú)立于用戶數(shù)據(jù)之外,便于把鏈表的操作獨(dú)立出來(lái),和具體數(shù)據(jù)節(jié)點(diǎn)無(wú)關(guān),這里可能有些人會(huì)問(wèn),數(shù)據(jù)節(jié)點(diǎn)怎么訪問(wèn)呢?  內(nèi)核通過(guò)一個(gè)container_of的宏從鏈表節(jié)點(diǎn)找到數(shù)據(jù)節(jié)點(diǎn)起始地址:




          找到數(shù)據(jù)節(jié)點(diǎn)起始地址后,通過(guò)數(shù)據(jù)節(jié)點(diǎn)定義就可以訪問(wèn)數(shù)據(jù)了,內(nèi)核紅黑樹(shù)rbtree也是同樣的設(shè)計(jì)。


          設(shè)備驅(qū)動(dòng)框架設(shè)計(jì)模式

           

          從Linux2.6開(kāi)始Linux加入了一套驅(qū)動(dòng)管理和注冊(cè)機(jī)制—platform平臺(tái)總線驅(qū)動(dòng)模型:



          當(dāng)調(diào)用platform_device_register(或platform_driver_register)注冊(cè)platform_device(或platform_driver)時(shí),首先會(huì)將其加入platform總線上,依次匹配platform總線上的platform_driver(或platform_device),然后調(diào)用platform_driver的.probe函數(shù)。其中platform_device存放設(shè)備資源(硬件息息相關(guān)代碼,易變動(dòng)),platform_driver則使用資源(比較穩(wěn)定的代碼),這樣當(dāng)改動(dòng)硬件資源時(shí),我們的上層使用資源的代碼部分幾乎可以不用去改動(dòng)。


          這里設(shè)計(jì)通過(guò)中間bus層,把強(qiáng)耦合Device和對(duì)應(yīng)Driver進(jìn)行了解耦隔離,定好match,probe等標(biāo)準(zhǔn)通信接口,就可以獨(dú)立開(kāi)發(fā),通過(guò)總線bus進(jìn)行關(guān)聯(lián)通信,有點(diǎn)類似中介模式。



          C++ Idioms(設(shè)計(jì)習(xí)語(yǔ))


          由于篇幅優(yōu)先,這里列舉一些非常重要且非常實(shí)用的C++專有的設(shè)計(jì)模式。


          RAII-Resource Acquisition Is Initialization


          ‘資源獲取即初始化‘(簡(jiǎn)稱 RAII)是C++防止內(nèi)存泄露一個(gè)很好解決方案,它結(jié)合構(gòu)造函數(shù)和析構(gòu)函數(shù),把資源生命周期和對(duì)象生命周期綁定起來(lái),在構(gòu)造函數(shù)中獲取資源(這些錯(cuò)誤會(huì)引發(fā)異常),然后將其釋放到析構(gòu)函數(shù)中(永不拋出),并且不需要顯式清理,從而防止忘記釋放資源;


           

          C ++STL庫(kù)很多類遵循RAII設(shè)計(jì)原則,比如std :: string,std :: vector,std :: thread等。

          Policy-based class Design


          基于策略設(shè)計(jì)又名policy-based class design 是一種基于C++計(jì)算機(jī)程序設(shè)計(jì)模式,以策略(Policy)為基礎(chǔ),并結(jié)合C++的模板元編程。就是將原本復(fù)雜的系統(tǒng),拆解成多個(gè)獨(dú)立運(yùn)作的“策略類別”,每一組policy class都只負(fù)責(zé)單純?nèi)缧袨榛蚪Y(jié)構(gòu)的某一方面。多重繼承由于繼承自多組 Base Class,故缺乏型別消息,而Templetes基于型別,擁有豐富的型別消息。多重繼承容易擴(kuò)張,而Templetes的特化不容易擴(kuò)張。Policy-Based Class Design 同時(shí)使用了 Template 以及 Multiple Inheritance 兩項(xiàng)技術(shù),結(jié)合兩者的優(yōu)點(diǎn),看下面例子:



          ResourceManager則稱為宿主類別(host class),只需要切換不同 Policy Class(ReadPolicy or WritePolicy),就可以得到不同的功能實(shí)體。Policy不一定要被宿主繼承,只需要用委托完成這一工作。但policies必須遵守一個(gè)隱含的constraint,接口必須一樣,故參數(shù)不能有巨大改變,policy 的一個(gè)重要的特征是,宿主類別經(jīng)常(并不一定要)使用多重繼承的機(jī)制去使用多個(gè) policy classes.  因此在進(jìn)行 policy 拆解時(shí),必須要盡可能達(dá)成正交分解,policy之間最好彼此獨(dú)立運(yùn)作,不相互影響。


          Pimpl - Pointer to implementation



          Pimpl是一種廣泛使用的削減編譯依賴項(xiàng)的技術(shù), 看下面例子可能就明白了:

            


          因?yàn)閃idget的成員變量有std::string,std::vector和Gadget,那么這些類型的頭文件在Widget編譯時(shí)必須出現(xiàn),這意味Widget的用戶必須包含“gadget.h”。這些增加的頭文件會(huì)增加Widget用戶的編譯時(shí)間,而且這使得用戶依賴于這些頭文件,即如果某個(gè)頭文件的內(nèi)容被改變了,Widget的用戶就要重新編譯。標(biāo)準(zhǔn)庫(kù)頭文件不會(huì)經(jīng)常改變,但是“gadget.h”可能會(huì)經(jīng)常修改。所以需要Pimp技術(shù)來(lái)消除這種變化影響--隔離變化


           


          這樣Widget頭文件里面就不需要包含“gadget.h”文件了,再CPP文件中再聲明具體的類型:


            


          在這里,我展示了“#include”指令,只為了說(shuō)明所有對(duì)頭文件的依賴(即std::string,std::vector和Gadget)依然存在。不過(guò)呢,依賴已經(jīng)從“widget.h”(Widget用戶可見(jiàn)的和使用的)轉(zhuǎn)移到“widget.cpp”(只有Widget的實(shí)現(xiàn)者?????????????????????????才能看見(jiàn)和使用),這樣就把widget頭文件變化影響隔離在內(nèi)部實(shí)現(xiàn)中,對(duì)外接口不變,這里就體會(huì)到這種設(shè)計(jì)模???????????????式的好處


          CRTP -The curiously recurring template pattern 


          CRTP (奇異遞歸模板模式)是一種在編譯期實(shí)現(xiàn)多態(tài)方法,是對(duì)運(yùn)行時(shí)多態(tài)一種優(yōu)化,多態(tài)是個(gè)很好的特性,但是動(dòng)態(tài)綁定比較慢,因?yàn)橐樘摵瘮?shù)表。而使用 CRTP,完全消除了動(dòng)態(tài)綁定,降低了繼承帶來(lái)的虛函數(shù)表查詢開(kāi)銷。

          CRTP包含:

          • 從模板類繼承,

          • 使用派生類本身作為基類的模板參數(shù)。???????????????????????????????????????

           


          這樣做的目的是在基類中使用派生類。從基礎(chǔ)對(duì)象的角度來(lái)看,派生對(duì)象本身就是對(duì)象,但是是向下轉(zhuǎn)換的對(duì)象。因此,基類可以通過(guò)將static_cast自身放入派生類來(lái)訪問(wèn)派生類.


          總結(jié)



          為什么要掌握設(shè)計(jì)模式,軟件危機(jī)帶來(lái)剛性要求,設(shè)計(jì)模式提倡的高內(nèi)聚,低耦合,代碼復(fù)用,可擴(kuò)展性等思想,可以給我們軟件設(shè)計(jì)帶來(lái)一些思考,有了思考,就會(huì)產(chǎn)生一些積極變化;



          理解設(shè)計(jì)模式前提,是要理解背后的設(shè)計(jì)原則,這是整個(gè)設(shè)計(jì)模式的精華;



          經(jīng)典的設(shè)計(jì)模式包含22種設(shè)計(jì)模式(沒(méi)有解釋器模式,日常開(kāi)發(fā)中,很少使用),大致分為三類:創(chuàng)建型模式,結(jié)構(gòu)型模式,行為模式;



          Linux系統(tǒng)里面包含大量設(shè)計(jì)模式思想,面向?qū)ο笤O(shè)計(jì),List/Rbtree抽象設(shè)計(jì),驅(qū)動(dòng)框架bus總線解耦設(shè)計(jì),都值得我們學(xué)習(xí);



          每種編程語(yǔ)言都會(huì)有一些獨(dú)特特殊習(xí)慣用法,Java的MVC,Golang的對(duì)象池模式(Object Pool)等,文中列舉的C++一些常見(jiàn)的慣用法RAII,Policy-based Design ,Pimpl,CRTP等,對(duì)C++開(kāi)發(fā)來(lái)說(shuō),了解和掌握他們,對(duì)于特定場(chǎng)景問(wèn)題多了一些好的解決方案;



          設(shè)計(jì)模式是銀彈嗎?不是,就像軟件工程也不是銀彈一樣,這些都只是工具,關(guān)鍵還是看是否真正理解其背后反射出的設(shè)計(jì)精髓,我們需要多一些批判性的思考,沒(méi)有絕對(duì)好壞,軟件設(shè)計(jì)的最終方案很多時(shí)候都是權(quán)衡(trade-off)結(jié)果,但我們的長(zhǎng)期目標(biāo)始終沒(méi)有變化。


          瀏覽 59
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  性色网址| 中文日韩欧美 | 日本免费A片视频 | 国产色情 免费 | 久久久精品网站 |