從MVC架構到C++的多態實現__C++

來源:互聯網
上載者:User

轉自:http://blog.csdn.net/historyasamirror/article/details/5025061

學習可以是一件很快樂的事,特別是當你發現以前所學的點點滴滴慢慢地能夠串起來或者變成了一個環,這種感覺真好。這篇文章就這麼來的。

 

從MVC架構開始說起吧。這兩天系統瞭解了一下MVC架構的內容,主要參考於文獻【1】。

MVC在這幾年應該被非常多的人所熟悉了,因為相當多的web架構採用的是這套架構,此外,早在MFC橫行的年代,MFC所採用的document/view架構也是MVC架構的變種。包括QT,它的model/view亦是如此。只不過它們都將MVC中的view和controller的功能整合到了一起。

MVC的全稱是model-view-controller architecture,最早被用在了smalltalk語言中。MVC最適合用在互動應用程式中。

我個人認為,理解MVC架構最重要的是兩點:

1. MVC將資料的維護和資料的呈現,與使用者的互動割裂了。Model負責的是資料的維護,就好比是DB和檔案要儲存資料一樣,可以認為它是process。而view負責的是資料的呈現,把資料通過某種形式在使用者面前展現,把它看做是output。model和view的關係就像下面這幅圖一樣。

 

而controller負責的是處理使用者的輸入。它提供一個視窗或者是控制項供使用者輸入資料,它就是input。所以,一個MVC架構,就是將互動式程式的process,input和output解耦合。

 

2. 這一點更為重要,就是model與view和controller之間的聯絡。任何一個MVC架構都必須提供一個“change-propagation mechenism”(變更-傳播機制)。而這個變更-傳播機制是MVC架構中model和view以及controller之間的唯一的聯絡(The change-propagation mechanism  is  the  only  link  between  the model and the views and controllers)。比如說,一個使用者通過controller改變了model中的資料,那麼model會使用變更-傳播機制來通知和它有關的view和controller,使得view去重新整理資料和重新顯示。

有很多人總是對於MVC架構不能夠熟練掌握,核心問題就在於他在使用MVC架構的時候是看不到變更-傳播系統的,所以對於MVC架構的內在運行機制無法瞭解。

 

完整過程如圖所示:

 

 

1. 使用者操作controller,相當於調用controller的handleEvent函數;

2. handleEvent函數實際上調用的是model中的成員函數,對model中的資料進行操作;

3. model中的資料被調用完成後,model會執行自身的notify函數;

4. notify函數會調用和這個model有關的所有view和controller的update函數;

5. update函數會調用各自的display函數和getData函數;

6. 當這一切都完成時,handleEvent才返回;

 

更多的關於MVC的內容就不在這篇文章中詳述了,畢竟俺寫這文章不是光為了MVC。有興趣的可以查看網路文檔或者參考文獻。

 

下面的重點在於討論這個change-propagation mechenism的實現。

 

其實一個簡單MVC架構的變更-傳播機制採用observer模式+多態就可以搞定了。model維護一個基類view(和controller)的指標隊列,將所有和這個model相關的派生view的指標放在這個隊列中。那麼model的notify函數就是依次調用隊列中的指標的update成員函數。

但是,在實際的C++的MVC系統中,比如MFC,或者QT,都沒有採用這種方法來實現變更-傳播機制,事實上,他們在實現這個機制的時候都沒用到多態。MFC採用的是訊息映射的機制,基本概念是建了一個訊息尋找表,將訊息和對應函數的映射關係儲存下來。每次處理一個訊息的時候,都去表中尋找到對應的函數,然後回調。而QT採用的signal-slot機制(具體的實現機制我不清楚,但肯定不是用的多態)。

為什麼MFC和QT都不採用多態呢。我相信有很多的原因,比如QT的signal-slot要求是能夠跨進程的,這肯定不是用多態能做到的。在這我只討論一個原因。

 

討論之前先說一說C++的多態機制的實現(我更推薦你看參考文獻【2】而不是我的這段話,【2】中把這個問題解釋得非常清楚)。很多人都知道vtable,這裡放著某個類的一個虛函數指標數組,某個類的指標如果要調用虛函數,先會通過vptr找到vtable,然後尋找到對應的函數。這個機制本身沒有問題。但關鍵是,C++在vtable中儲存的是所有虛函數的指標,也就是說,如果一個基類有1000個虛函數,但它的繼承類只改寫了其中的5個,那麼這個繼承類的vtable中仍然有1000項,表中的995項被浪費了。正是由於這個原因,MFC和QT都沒有採用C++的多態機制來實現變更-傳播機制。因為在MFC和QT中,它的每個基類都有著大量的虛函數,而在實際應用當中,繼承類可能只是改寫其中的很少的幾項,如果採用多態實現,那麼會浪費大量的記憶體空間。

借用文獻【2】的一段話,“也正 是因為這個原因,從OWL 到VCL,.. 從MFC到Qt,以至於近幾年出現的GUI和遊戲開發架構,所有涉及大量事件行為的C++ GUI Framework沒有一家使用標準的C++多態技術來構造視窗類別層次,而是各自為戰,發明出五花八門的技術來繞過這個暗礁。其中比較經典的解決方案有 三,分別以VCL 的動態方法、MFC的全域事件尋找表和Qt 的Signal/Slot為代表。而其背後的思想是一致的,用Grady Booch的一句話來總結,就是:“當你發現系統中需要大量相似的小型類的時候,應當用大量相似的小型對象解決之。” 也就是說,將一些本來會導致需要派生新類來解決的問題,用執行個體化新的對象來解決。這種思路幾乎必然導致類似C#中delegate那樣的機製成為必需品。 可惜的是,標準C++ 不支援delegate。雖然C++社群裡有很多人做了各種努力,應用了諸如template、functor等進階技巧,但是在效果上距離真正的 delegate還有差距。因此,為了保持解決方案的簡單,Borland C++Builder擴充了__closure關鍵字,MFC發明出一大堆怪模怪樣的宏,Qt搞了一個moc前處理器,八仙過海,各顯神通。”

 

結語

以上論點其實我並沒有十足的把握,因為正如我所說的,實際中不採用多態可能有方方面面的考慮,而我所提到的這個原因也許微不足道。

為了反駁我自己的觀點,可以計算一下,當MFC最開始誕生的時候,那個時候電腦的記憶體很小,所以大家很節約,但現在,電腦的記憶體非常大,一個vtable就算是上千個函數指標,那也是可以忽略不計的。

 

 

參考文獻

【1】  面向模式的軟體體繫結構 卷1:模式系統

【2】 C++多態技術的實現和反思

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.