難度:
現代程式開發最關注的一件事之一就是如何提高代碼的可複用性和可維護性。
OOP提供了抽象概念來構建一個統一的世界模型,具像的物理對象被實現在這個簡單的抽象模型體系中,最終形成龐大的樹型結構來覆蓋由該抽象涉及到的方方面面,可複用性由此誕生。
GP提供了另類的複用方法,讓演算法與類型無關。
在大多數的開發中,GP的運用幾乎為空白,其實這並不是壞現象,除了讓部分代碼變得稍微優雅,剩下的可以說在大部分情況下,GP都是畫蛇添足。在開發中, 我們努力用OO的知識和經驗來建立類,讓這些類對象相互協同工作來達到目的,一般情況下,我們是處在一個很小的問題域中著手解決具體的問題,不需要更多的
思考,所以template關鍵字幾乎可以不用出現在我們編寫的代碼當中。
在以往的觀點中,GP和OOP就像兩條平行線,一邊是靜態多態,一邊是動態多態,這樣的觀點讓很多人誤解了GP,加上在實際工作中,幾乎不寫template關鍵字,從而加深了這種誤解。
到底GP存在的意義在哪裡?相信大多數C++程式員都會用到STL,正是GP造就了STL的應用如此廣泛。GP是一種用來開發高效,高複用程式庫的編程範式,所以在實際工作的大多數項目中,GP對我們來說可有可無。用GP來分析問題的三個步驟:
1,將抽象問題域分類成Concepts.
在STL中,迭代器按行為被分類成5類。Input/Output/Forward/Bidirectional/Random
Access Iterator。
2,基於這些Concepts,設計泛型演算法。
例如std::generate要求Forward Iterator,而std::next_perutation要求Bidirectional Iterator.
3, 建立Concepts的具體模型。
例如,std::vector的Iterator,std::map的Iterator。
其實GP的分析方式與OOP極其類似,都是由具體的事物尋找抽象,然後按抽象來設計,最後基於抽象實現具象。
那GP編寫的泛型程式庫到底好在哪裡?
可複用性:
我們嘗試用OO來實現vector
- class oo_vector
- {
- public:
- void push_back(vec_elem_t& r)
- {
- vec_elem_t * n = r.clone(*this);
- //hold n ...
- }
- };
如果我們要使用oo_vector,那麼儲存的元素類型必須由vec_elem_t派生並實現clone方法。如果用C++ Templates來實現,複雜度自然降低。至少我們的元素類型不會出現派生自vec_elem_t, map_elem_t, list_elem_t等等這種龐大的多重繼承的情況。
粘和性:
用上面的oo_vector來思考。如果有一個現成的類,要讓oo_vector來操作,該怎麼辦?C++ Templates應用到這種情況下大大提高了粘和性和可複用性。
Templates把C++變得太“泛”了?
C++是強型別語言,繼承確定了兩個類的關係,這種關係可以某些方法作用於子類對象上,他們之間存在著一種限制。而對於一個泛型演算法,它能接納的模板參數 T,從形式上,看不到任何限制,但這也許會使你的代碼編譯失敗。例如把std::list的iterator傳遞到std::find中,得到的編譯錯誤是 iterator缺少某某東西,在這種情況下,如果編譯錯誤能提示list的iterator不能用在find裡,這樣就會友好很多。其實這不僅僅只是提示得友好,因為我們在設計這種泛型演算法的時候就是依據上面提到的Concepts,這種
Concepts在目前的C++語言中,只是一個潛在限制,作為泛型庫的使用者,很容易忽略掉這種限制。或許有人會認為,如果C++引入Concepts 的限制來做類型的檢查,那麼它還會是GP嗎?它仍然是GP,顯而易見的,泛型庫的設計是從Concepts出發,這好比一個子類是從基類出發一樣。
Concepts會是畫蛇添足嗎?
就算語言引入了Concepts,那麼list的iterator仍然不能用到find中去,這和沒有Concepts的情況完全一樣。Concepts 描述了一組抽象的行為,我們可以提供一個演算法函數來處理具有這種行為的類型,它的引入會把人們的關注目光從圍繞T上轉移到Concept上,這能更好地表達每個抽象的核心思想。按Concepts來特化演算法,比按類型來特化演算法是更好GP,因為GP原本就與類型無關。在解決list::iterator應用到find的問題上,只需要特化一個符合list::iterator Concept的find即可。
現在看來GP和OOP似乎很相似了,從分析的角度來說,他們的方向是一樣,只是方法不同。這兩個範式的區別不像OOP和面向過程那樣,立竿見影。或許說,GP和OOP都應該是一種“泛型”,GP是基於行為概念的抽象,OOP則是基於事物概念的抽象。在OO的設計中,我們仍然會考慮到行為這一問題,只不過從OO的角度來說,這太隱晦,而GP在這方面則表達得更好,更抽象。