1.初識迭代器模式
提供一種方法順序訪問一個聚合對象中各個元素椎扬,而又不需暴露該對象的內(nèi)部表示。
- Iterator:迭代器接口。定義訪問和遍歷元素的接口掉弛。
ConcreteIterator:具體的迭代器實現(xiàn)對象戒财。實現(xiàn)對聚合對象的遍歷热监,并跟蹤遍歷時的當(dāng)前位置。
Aggregate:聚合對象饮寞。定義創(chuàng)建相應(yīng)迭代器對象的接口孝扛。
ConcreteAggregate:具體聚合對象列吼。實現(xiàn)創(chuàng)建相應(yīng)的迭代器對象。
2.體會迭代器模式
2.1 場景問題——工資表數(shù)據(jù)的整合
這個項目的背景是這樣的苦始,項目的客戶方收購了一家小公司寞钥,這家小公司有自己的工資系統(tǒng),現(xiàn)在需要整合到客戶方已有的工資系統(tǒng)上陌选。
現(xiàn)在除了要把兩個工資系統(tǒng)整合起來外理郑,老板還希望能夠通過決策輔助系統(tǒng)來統(tǒng)一查看工資數(shù)據(jù),他不想看到兩份不同的工資表咨油。那么應(yīng)該如何實現(xiàn)呢您炉?
有何問題:
本來就算內(nèi)部描述形式不一樣,只要不需要整合在一起役电,兩個系統(tǒng)單獨輸
出自己的工資表赚爵,是沒有什么問題的。但是宴霸,老板還希望能夠以一個統(tǒng)一的方式來查看所有的工資數(shù)據(jù)囱晴,也就是說從外部看起來,兩個系統(tǒng)輸出的工資表應(yīng)該是一樣的瓢谢。
經(jīng)過分析畸写,要滿足老板的要求,而且要讓兩邊的系統(tǒng)改動都盡可能的小的
話氓扛,問題的核心就在于如何能夠以一種統(tǒng)一的方式來提供工資數(shù)據(jù)給決策輔助系統(tǒng)枯芬,換句說來說就是:如何能夠以一個統(tǒng)一的方式來訪問內(nèi)部實現(xiàn)不同的聚合對象。
2.2 使用模式的解決方案
3.理解迭代器模式
3.1 認識迭代器模式
3.1.1 迭代器模式的功能
迭代器模式的功能主要在于提供對聚合對象的迭代訪問采郎。迭代器就圍繞著
這個“訪問”做文章千所,延伸出很多的功能來。比如:
- 1)以不同的方式遍歷聚合對象蒜埋,比如向前淫痰、向后等
- 2)對同一個聚合同時進行多個遍歷
- 3)以不同的遍歷策略來遍歷聚合,比如是否需要過濾等
- 4)多態(tài)迭代整份,含義是:為不同的聚合結(jié)構(gòu)待错,提供統(tǒng)一的迭代接口,也就是說通過一個迭代接口可以訪問不同的聚合結(jié)構(gòu)烈评,這就叫做多態(tài)迭代火俄。上面的示例就已經(jīng)實現(xiàn)了多態(tài)迭代,事實上讲冠,標(biāo)準(zhǔn)的迭代模式實現(xiàn)基本上都是支持多態(tài)迭代的瓜客。
但是請注意:多態(tài)迭代可能會帶來類型安全的問題,可以考慮使用泛型
3.1.2 迭代器模式的關(guān)鍵思想
聚合對象的類型很多,如果對聚合對象的迭代訪問跟聚合對象本身融合在一起的話谱仪,會嚴重影響到聚合對象的可擴展性和可維護性玻熙。
因此迭代器模式的關(guān)鍵思想就是把對聚合對象的遍歷和訪問從聚合對象中分離出來,放入到單獨的迭代器中芽卿,這樣聚合對象會變得簡單一些揭芍;而且迭代器和聚合對象可以獨立的變化和發(fā)展,會大大加強系統(tǒng)的靈活性卸例。
3.1.3 內(nèi)部迭代器和外部迭代器
所謂內(nèi)部迭代器称杨,指的是由迭代器自己來控制迭代下一個元素的步驟,客戶端無法干預(yù)筷转,因此姑原,如果想要在迭代的過程中完成工作的話,客戶端就需要把操作傳給迭代器呜舒,迭代器在迭代的時候會在每個元素上執(zhí)行這個操作锭汛,類似于Java的回調(diào)機制。
所謂外部迭代器袭蝗,指的是由客戶端來控制迭代下一個元素的步驟唤殴,像前面的示例一樣,客戶端必須顯示的調(diào)用next來迭代下一個元素到腥。
總體來說外部迭代器比內(nèi)部迭代器要靈活一些朵逝,因此我們常見的實現(xiàn)多屬于外部迭代器,前面的例子也是實現(xiàn)的外部迭代器乡范。
3.1.4 Java中最簡單的統(tǒng)一訪問聚合的方式
如果只是想要使用一種統(tǒng)一的訪問方式來訪問聚合對象配名,在Java中有更簡單的方式,簡單到幾乎什么都不用做晋辆,利用Java 5以上版本本身的特性即可渠脉。
但是請注意,這只是從訪問形式上一致了瓶佳,但是也暴露了聚合的內(nèi)部實現(xiàn)芋膘,因此并不能算是標(biāo)準(zhǔn)迭代器模式的實現(xiàn),但是從某種意義上說霸饲,可以算是隱含的實現(xiàn)了部分迭代器模式的功能索赏。
3.2 使用Java的迭代器
大家都知道,在java.util包里面有一個Iterator的接口贴彼,在Java中實現(xiàn)迭
代器模式是非常簡單的,而且java的集合框架中的聚合對象埃儿,基本上都是提供了迭代器的器仗。
下面就來把前面的例子改成用Java中的迭代器實現(xiàn),一起來看看有些什么改變。
- 1)不再需要自己實現(xiàn)的Iterator接口精钮,直接實現(xiàn)java.util.Iterator接口就可以了威鹿,所有使用自己實現(xiàn)的Iterator接口的地方都需要修改過來
- 2)Java中Iterator接口跟前面自己定義的接口相比,需要實現(xiàn)的方法是不一樣的
- 3)集合已經(jīng)提供了Iterator轨香,那么CollectionIteratorImpl類就不需要了忽你,直接去掉
3.3 帶迭代策略的迭代器
由于迭代器模式把聚合對象和訪問聚合的機制實現(xiàn)了分離,所以可以在迭代器上實現(xiàn)不同的迭代策略臂容,最為典型的就是實現(xiàn)過濾功能的迭代器。
在實際開發(fā)中,對于經(jīng)常被訪問的一些數(shù)據(jù)可以使用緩存姑裂,把這些數(shù)據(jù)存放在內(nèi)存中到旦。但是不同的業(yè)務(wù)功能需要訪問的數(shù)據(jù)是不同的,還有不同的業(yè)務(wù)訪問權(quán)限能訪問的數(shù)據(jù)也是不同的球散,對于這種情況尿赚,就可以使用實現(xiàn)過濾功能的迭代器,讓不同功能使用不同的迭代器來訪問蕉堰。當(dāng)然凌净,這種情況也可以結(jié)合策略模式來實現(xiàn)。
在實現(xiàn)過濾功能的迭代器中屋讶,又有兩種常見的需要過濾的情況冰寻,一種是對數(shù)據(jù)進行整條過濾,比如只能查看自己部門的數(shù)據(jù)丑婿;另外一種情況是對數(shù)據(jù)進行部分過濾性雄,比如某些人不能查看工資數(shù)據(jù)。
3.4 誰定義遍歷算法的問題
在迭代器模式的實現(xiàn)中羹奉,常見有兩個地方可以來定義遍歷算法秒旋,一個就是聚合對象本身,另外一個就是迭代器負責(zé)遍歷算法诀拭。
在聚合對象本身定義遍歷的算法這種情況下迁筛,通常會在遍歷過程中,用迭代器來存儲當(dāng)前迭代的狀態(tài)耕挨,這種迭代器被稱為游標(biāo)细卧,因為它僅用來指示當(dāng)前的位置。
在迭代器里面定義遍歷算法筒占,會易于在相同的聚合上使用不同的迭代算法贪庙,同時也易于在不同的聚合上重用相同的算法。比如上面帶策略的迭代器的示例翰苫,迭代器把需要迭代的數(shù)據(jù)從聚合對象中取出并存放到自己對象里面止邮,然后再迭代自己的數(shù)據(jù)这橙,這樣一來,除了剛開始創(chuàng)建迭代器的時候需要訪問聚合對象外导披,真正迭代過程已經(jīng)跟聚合對象無關(guān)了屈扎。
3.5 雙向迭代器
所謂雙向迭代器的意思就是:可以同時向前和向后遍歷數(shù)據(jù)的迭代器。
在Java util包中的ListIterator接口就是一個雙向迭代器的示例撩匕,當(dāng)然自己實現(xiàn)雙向迭代器也非常容易鹰晨,只要在自己的Iterator接口中添加向前的判斷和向前獲取值的方法,然后在實現(xiàn)中實現(xiàn)即可止毕。
3.6 迭代器模式的優(yōu)缺點
- 更好的封裝性
- 可以以不同的遍歷方式來遍歷一個聚合
- 迭代器簡化了聚合的接口
- 簡化客戶端調(diào)用
- 同一個聚合上可以有多個遍歷
4.思考迭代器模式
4.1 迭代器模式的本質(zhì)
控制訪問聚合對象中的元素
4.2 何時選用
- 1)如果你希望提供訪問一個聚合對象的內(nèi)容模蜡,但是又不想暴露它的內(nèi)部表示的時候,可以使用迭代器模式來提供迭代器接口滓技,從而讓客戶端只是通過迭代器的接口來訪問聚合對象哩牍,而無需關(guān)心聚合對象內(nèi)部實現(xiàn)。
- 2)如果你希望有多種遍歷方式可以訪問聚合對象令漂,可以使用迭代器模式
- 3)如果你希望為遍歷不同的聚合對象提供一個統(tǒng)一的接口膝昆,可以使用迭代器模式
4.3 翻頁迭代
在實際開發(fā)中很常用的翻頁功能的實現(xiàn),常見的翻頁功能有如下幾種實現(xiàn)方式:
- 1)純數(shù)據(jù)庫實現(xiàn)依靠SQL提供的功能實現(xiàn)翻頁叠必,用戶每次請求翻頁的數(shù)據(jù)荚孵,就會到數(shù)據(jù)庫中獲取相應(yīng)的數(shù)據(jù)
- 2)純內(nèi)存實現(xiàn)就是一次性從數(shù)據(jù)庫中把需要的所有數(shù)據(jù)都取出來放到內(nèi)存中,然后用戶請求翻頁時纬朝,從內(nèi)存中獲取相應(yīng)的數(shù)據(jù)
- 3)上面兩種方式各有優(yōu)缺點:
第一種方案明顯是時間換空間的策略收叶,每次獲取翻頁的數(shù)據(jù)都要訪問數(shù)據(jù)庫,運行速度相對比較慢共苛,而且很耗數(shù)據(jù)庫資源判没,但是節(jié)省內(nèi)存空間。
第二種方案是典型的空間換時間隅茎,每次是直接從內(nèi)存中獲取翻頁的數(shù)據(jù)澄峰,運行速度快,但是很耗內(nèi)存辟犀。 - 4)純數(shù)據(jù)庫實現(xiàn) + 純內(nèi)存實現(xiàn)
思路是這樣的:如果每頁顯示10條記錄俏竞,根據(jù)判斷,用戶很少翻到10頁過
后堂竟,那好了魂毁,第一次訪問的時候,就一次性從數(shù)據(jù)庫中獲取前10頁的數(shù)據(jù)出嘹,也就是100條記錄席楚,把這100條記錄放在內(nèi)存里面。
這樣一來税稼,當(dāng)用戶在前10頁內(nèi)進行翻頁操作的時候烦秩,就不用再訪問數(shù)據(jù)庫了刁赦,而是直接從內(nèi)存中獲取數(shù)據(jù),這樣速度就快了闻镶。
當(dāng)用戶想要獲取第11頁的數(shù)據(jù),這個時候才會再次訪問數(shù)據(jù)庫丸升,對于這個時候到底獲取多少頁的數(shù)據(jù)铆农,簡單的處理就是繼續(xù)獲取10頁的數(shù)據(jù),比較好的方式就是根據(jù)訪問統(tǒng)計進行衰減訪問狡耻,比如折半獲取墩剖,也就是第一次訪問數(shù)據(jù)庫獲取10頁的數(shù)據(jù),那么第二次就只獲取5頁夷狰,如此操作直到一次從數(shù)據(jù)庫中獲取一頁的數(shù)據(jù)岭皂。這也符合正常規(guī)律,因為越到后面沼头,被用戶翻頁到的機會也就越小了爷绘。