Iterator模式的幾種用法

oxygen發表於2007-06-12
Iterator模式的幾種用法
在網路上看帖子時發現不少模式的初學者對Iterator模式的理解僅僅停留在從類庫的容器類取得Iterator來遍歷容器中的內容的程度。
因此在這裡寫幾個例子,來加深大家對Iterator模式的理解。

對容器中元素的訪問涉及到3個方面。
1.容器的型別
2.檢索容器內元素的方法
3.對元素的操作
比如說我們有一個表示書店的book_store類。裡面儲存了各種各樣的book類的例項。
book類有name和type兩種屬性。表示書的名字和類別。
因此book_store類內部會用一個容器來儲存book的例項。比如list。
class book
{
public:
string name;
string type;
};

class book_store
{
private:
list<book> m_books;
};

我們現在有這樣一個簡單的應用,那就是輸出所有的書名到螢幕。
那麼我們來看一下在引入Iterator模式前有那幾種實現方法,各有什麼缺點。
1.給book_store類增加一個print_book_name的函式。來實現這個功能。
這樣做的缺點有兩個:
(1).將輸入輸出邏輯和業務物件繫結在一起,導致了今後系統難以變更。
(2).輸出邏輯和內部實現繫結。
比如現在要把輸出到螢幕改成輸出到log檔案的話,那麼就需要修改print_book_name了。
2.給book_store類增加一個get_list函式。然後寫一個print_book類來負責列印書名的列表。這是一個初學者常用的方式。表面上看來似乎兩個類各擔其責,一個表示業務物件一個負責列印。
如果需要列印到log檔案的話,只要新增加一個log_book類,book_store和print_book都不受影響。解決了上面的這個問題。
這樣做的缺點是:
(1).把book_store類的內部實現給暴露出來了,違反了封裝的原則。
如果現在把內部容器型別從list換成了vector的話,就要修改輸出邏輯了。也就是說,要同時改寫print_book類和log_book類。
3.增加一個list和vector類共同的基類/介面,比如I_container。然後給book_store類增加一個get_books函式,返回I_container。
這在一定程度上解決了上面的問題。但是並不徹底。應為還是暴露了內部的實現,只不過從list上升到了I_container。
· 如果現在系統發生了變化,book_store不再在本地儲存books了,而是需要透過網路取得的話,print_book和log_book就無法對應了。
· 另一個限制是,我需要一個新的機能,那就是列印所有type為”烹飪書”的機能的話,就需要一個print_cook_book類了,而這裡邊的格式化程式碼和輸出程式碼和print_book是相同的。重複程式碼是維護的噩夢。

接下來我們看一下Iterator模式如何解決以上的這些問題。
首先,我們引入一個I_iterator的Interface。
然後建立一個I_iterator的實現類all_book_iterator。這個類可以迭代book_store中的所有book。
接下來建立一個print_book類,從I_iterator中取得每一個book,然後列印到螢幕。

下面我們看一下如何對應上面的這些需求變更。
1.要求輸出到log檔案
追加一個log_book類,其他都類都不需要改變。
2.將內部容器從list變成vector。
修改iterator中的相關程式碼。Iterator的使用者不會受到影響。
3.從網路取得資料。
修改iterator中的相關程式碼。Iterator的使用者不會受到影響。
4.按照type檢索
在iterator中新增一個type的屬性,或者是構造一個新的type_search_iterator。
在iterator中把不符合檢索條件的book過濾掉。
5.按照某一特定順序輸出book名到螢幕,或是log檔案。
實現一個按該順序輸出的iterator。
6.以同樣的方式列印cd_store。
實現對應於cd_store的iterator。

可見,iterator模式在各種iterator中封裝了檢索元素的方法。
將容器和對容器中元素的操作完全隔開。
大大增加了程式碼的可重用性。

相關文章