上篇博客咱們從醋溜土豆絲與清炒苦瓜中認識了「模板方法模式」,那麼在今天這篇博客中咱們要從電影院中來認識"迭代器模式"(Iterator Pattern)。「迭代器模式」顧名思義就是經過迭代的形式來取出容器中的值。若是你對Java語言熟悉的話,那麼你應該使用過Java中的迭代器,迭代器通常使用hasNext()方法來判斷是否有下一個值,若是有下一個值的話,那麼就使用next()方法來獲取下一個值。本篇博客中就從「電影院」中來認識一下這種「迭代器模式」,而且將數組與字典使用迭代器進行遍歷。具體說來使用迭代器的數組與字典對外所展示的遍歷方式是一致的,也就是說用戶在遍歷字典或者數組時,所使用的方法是一致的。當然咱們今天的任務是使用Swift語言去實現屬於Swift的迭代器,使用該迭代器來遍歷字典和數組。html
今天博客中所使用的場景與電影院有關,咱們假設在一家商場中有兩家電影院,而商場中間的大屏幕會滾動展現兩家電影院所放映的電影。兩家電影院的區別就是其存儲電影資源的方式不一樣,一個是使用數組來存儲的電影,一個是使用字典來存儲的電影。若是不使用迭代器模式的話,那麼兩家電影院所遍歷電影的方式確定是不一樣的。今天博客中咱們先給出無「迭代器模式」中遍歷兩家電影資源的方式,而後在給出「迭代器模式」下遍歷兩家電影院中電影資源的方式。固然,在下方使用迭代器遍歷時,咱們也使用了「工廠方法模式」,具體請參見下詳細實現。git
下方是迭代器模式的定義:github
迭代器模式:提供一種方法順序訪問一個聚合對象中的每一個元素,而又不暴漏其內部的表示。編程
1、無「迭代器」的電影院設計模式
1.無「迭代器」電影院的類圖數組
在本篇博客中的第一部分咱們先給出無「迭代器」的電影院遍歷其電影資源的方式。由於不一樣電影院存儲電影資源的方式不一樣,因此在沒有迭代器模式下二者的遍歷方式是不一樣的。在代碼設計中咱們一向遵循「依賴接口編程,而不依賴具體實現」的原則,下方雖然沒有使用「迭代器模式」,可是咱們仍是要定義接口的。下方的類圖中的CinemaType01接口就是咱們全部影院要實現的接口。在該協議中有一個display()方法,用來展現該影院正在放映的電影,而咱們的Market(商場)類就是依賴於這個影院接口,在Market的類中要調用具體影院的display()方法來展現放映的電影。要說明一點就是CinemaType01協議是咱們商場中給商場中的電影院所制定的規則,由於咱們的Market要調用display()方法將商場中的影院所熱播的電影投影到大屏幕上,只要是入駐該Market的影院就得遵循CinemaType01協議,並實現display()方法。由於影院存儲電影的方式不一樣因此display()的實現也不一樣。post
經過下方的類圖,咱們容易看到,Market雖然是調用具體影院的display()方法。可是Market不依賴於影院的具體實現,而是依賴於影院的接口,也就是下方的CinemaType01協議。Cinema01和Cinema01兩個類就是咱們商場中的兩個具體的電影院了。Cinema01中的items是個數組,Cinema02中的是個字典。沒有使用「迭代器」的商場影院的總體設計的類圖以下所示:測試
2. 代碼實現url
上面的類圖是咱們的設計,也就是相似於設計圖紙。接下來咱們開始搬磚,要對上面的設計圖紙進行實現,也就是咱們的代碼實現。因該示例比較簡單,因此在該部分咱們實現完畢後會給出相應的測試用例。測用例就至關於蓋好的商品房要作樣板間同樣呢。下方片斷就是咱們給出的代碼實現。spa
在下方代碼片斷中,最上方就是咱們全部電影院都要實現的協議CinemaType01,在該協議中咱們聲明瞭display()方法來展現當前該影院放映的電影。緊接着該協議下方的兩個實現就是咱們的兩個電影院Cinema01和Cinema02。在這兩個電影院中一個使用的Array來存儲的影片資源,一個使用的是Dictionary來存儲的電影資源。Cinema01中的display()所作的事情就是對數組的遍歷,而Cinema02所作的事情就是對字典的遍歷。而咱們的商場類Market就負責調用相應電影院中的display()方法來展現商場中電影院所熱播的電影。
在Market類中咱們須要注意一下,全部影院是存在cinemas數組中的。而咱們在聲明cinemas的數組類型時,爲該數組的泛型指定的是CinemaType01協議(也就是是接口),這說明cinemas存儲的是遵循CinemaType01協議的全部電影院,而不只僅是這兩個電影院。這樣若是來了第三家電影院就能夠無縫的給咱們商場的大屏幕進行對接了。下方就是咱們的具體實現。
三、測試用例
咱們搬完磚蓋完房子了,接下來到了測試的時間了,下方就是咱們的測試用例。由於該示例比較簡單,因此咱們的測試用例也是比較簡單的,就下邊簡簡單單的幾行代碼。雖然簡單,可是測試用例仍是比較簡單的。下方咱們建立了一個商場對象,而且給該商場對象指定了兩家電影院,也就是咱們商場中所入駐的電影院。而後咱們的商場會調用display()方法來打印所播放的電影,具體以下所示:
2、使用「迭代器」來規範商場中的影院
上面的設計彷佛沒有什麼問題,也便於後期的擴充,在來一家影院的話,只要實現商場中定義的CinemaType01規則便可,也就是實現display()方法便可。由於咱們的Market要調用這個display()方法。可是每一個電影院中存儲電影資源的方式不一樣,致使display()方法各有不一樣。商場爲了規範電影院,提出每一個電影院要使用同一個的display()方法。由於這個要求是經過迭代器來實現的,因此咱們將電影院中的display()方法進行了一個重命名,咱們將其重命名爲iteratorItem()。
下面我要引入迭代器,而後就能夠將每一個電影院中的display()方法進行提取了。咱們引入迭代器後將display()方法重命名爲iteratorItem()。咱們統一影院中的iteratorItem()方法的解決方案是引入迭代器。由於迭代器對外使用的方式是同樣的,咱們能夠爲不一樣的數據類型指定不一樣的迭代器,好比數組迭代器,字典迭代器。不管是什麼迭代器,外部使用該迭代器的方式是一致的,這樣就能夠作到不管使用什麼類型來存儲電影資源,只要使用迭代器遍歷,其遍歷方式是不變的。可能此處說的有些抽象,接下來將會給出具體的設計細節與實現細節,來直觀的感覺一下迭代器的魅力。有了迭代器咱們就可使用一個display()方法(也就是該部分的iteratorItem()方法)來遍歷不一樣類型的數據了。
1.引入「迭代器」後的電影院
仍是老套路,首先咱們會給出類圖的設計,而後再給出具體實現呢。下方就是咱們引入「迭代器」後的類圖。大眼一看也許會有些抽象,客官莫着急,聽我慢慢道來。在下方類圖中大致分爲三個模板一個是Market類,這個類與以前沒有什麼區別。綠框中是咱們引入的迭代器,黃框中是咱們重構後的電影院,在電影院使用迭代器後,咱們在此使用了工廠方法模式,具體請看下方詳述。
咱們先來看今天的主題,也就是迭代器的設計思路。下方綠框中是咱們引入的迭代器。固然咱們是依賴接口編程的,迭代器怎麼能沒有接口呢。在迭代器設計之初咱們先給出迭代器的協議,也就是綠框中的Iterator協議。該協議中有兩個方法,一個是hasNext(),用來判斷是否有下一項。另外一個是next()方法,若是有下一項,那麼就使用next()方法來獲取下一項。在遵循Iterator協議的基礎上咱們給出了數組迭代器ArrayIterator和字典迭代器DictionaryIterator的實現。在代碼實現中咱們會給出詳細的實現方式。
接着咱們看黃框中的部分,也就是咱們使用工廠方法模式重構後的電影院。在電影院協議CinemaType中定義了電影院中要實現的方法,其中的iteratorItem()方法就是咱們上一部分的display()方法。在引入迭代器後,全部電影院使用迭代器進行遍歷元素的方式都是同樣的(都是使用hasNext()和next()方法),全部咱們將iteratorItem()方法的默認實現放在了CinemaType協議的默認延展中。而createIterator()方法就是咱們的工廠方法,它負責建立相應的迭代器。createIterator()方法依賴於迭代器的接口而不依賴於迭代器的具體實現。若是是對數組進行遍歷,那麼該方法建立的就是數組迭代器,若是是對字典遍歷,那麼建立的就是字典迭代器。
2. 上方類圖的具體實現
接下來咱們有到了搬磚的時刻了,在該部分將會對上述類圖進行具體的代碼實現,固然咱們使用的仍然是Swift語言。若是你對其餘語言使用起來更爲駕輕就熟,你就使用你拿手的面嚮對象語言來實現呢。下方咱們會先給出迭代器的實現,而後在給出電影院的實現。Market類的結構基本不變。
(1)、實現咱們的迭代器
從上面類圖的綠框中咱們不難看出,咱們要先給出迭代器協議的實現,而後給出數組迭代器和字典迭代器的具體實現。下方代碼片斷就是對應着上方類圖中綠框部分的實現。Iterator協議中的內容比較簡單,就是聲明瞭外部使用迭代器的兩個方法:hasNext()和next()。
ArrayIterator就是咱們實現的數組迭代器。在ArrayIterator中對數組進行了遍歷,position記錄了下一個元素的下標,hasNext()中所作的事情就是經過position來判斷是否有下個元素。next()就是經過position來獲取數組中的值。DictionaryIterator就是咱們建立的字典迭代器,該迭代器的功能是對字典進行遍歷的。其中的position也是用來記錄下一個元素下標的,其中的allKeys數組用來獲取當前字典的全部key的,allKeys數組經過當前的position來獲取key,而後經過key獲取該字典相應的值。具體實現方式以下所示:
(2)、在影院中使用上述迭代器
接下來就是規範影院的時候到了,商場規定入駐本商城的電影商家只須要選擇相應的迭代器便可。遍歷的默認實現會在電影院協議的默認延展中給出。下方代碼片斷就是使用迭代器後的影院實現。
CinemaType協議就是商場規定的影院協議,其中定義了兩個方法,createIterator()方法負責來建立特定的迭代器,iteratorItem()方法則負責使用createIterator()建立的迭代器來變量相應的數據。此處的createIterator()方法就是咱們「工廠方法模式」中的工廠方法,也就是說在咱們設計影院結構時咱們使用了「工廠方法模式」。關於工廠方法模式的更多的細節,請參考以前的工廠模式主題的博客《設計模式(四):從「兵工廠」中探索簡單工廠、工廠方法和抽象工廠模式》,關於工廠模式在此就不作過多的贅述了。
extension CinemaType就是電影院協議的默認延展。其中給出了iteratorItme()方法的具體實現,該方法適用於全部迭代器的遍歷。由於全部迭代器都遵循於Iterator協議,都有hasNext()方法與next()方法,全部全部電影院均可以使用該方法來迭代遍歷本身的元素。
Cinema01和Cinema02這兩個類則是咱們電影院的具體實現。兩個類都遵循CinemaType協議,並給出了createIterator()方法。Cinema01使用的是數組來存儲的電影資源,全部建立的是數組迭代器。Cinema02使用的是字典存儲的電影資源,因此建立的是字典迭代器。不管建立什麼樣的類型的迭代器,iteratorItme()方法都是能夠正常使用的,這也就是使用迭代器的好處。
(3)、商場類與測試用例
通過上面的兩步,咱們已經將迭代器的核心實現完畢。下方的代碼就是咱們的Market類與測試用例。Market類幾乎沒有變化,咱們以前在Market中調用的是電影院中的display()方法,只不過如今咱們是調用電影院的iteratorItem()方法。而Market類的使用方式沒有任何的變化,也就是咱們的測試用例沒有任何的變化。下方就是咱們的Market類與測試用例以及輸出結果。
至此咱們的迭代器模式的完整實例已經實現完畢,其好處就是在於若是商場進入了第三家電影院,只須要遵循相應的協議並指定相應的迭代器便可。至於如何遍歷,交給咱們的默認實現來作。
今天博客中的代碼仍然會在Github上進行分享,分享地址爲:https://github.com/lizelu/DesignPatterns-Swift