場(chǎng)景1:
人物:大鳥(niǎo)、小菜
這天是周末,小菜和大鳥(niǎo)一早出門(mén)游玩兒,上了公交車(chē),車(chē)內很擁擠。
“上車(chē)的乘客請買(mǎi)票?!笔燮眴T一邊在人縫中穿插著(zhù),一邊說(shuō)道。
……
“先生,您這包行李太大了,需要補票的?!笔燮眴T對一位拿著(zhù)大包行李的乘客說(shuō)道。
“哦,這也需要買(mǎi)票呀,它又不是人?!睅Т蟀欣畹某丝驼f(shuō)。
“您可以看看規定,當您攜帶的行李占據了一個(gè)客用面積時(shí),請再購買(mǎi)同程車(chē)票一張,謝謝合作?!笔燮眴T指了指車(chē)上的紙牌子。
這位乘客很不情愿地再買(mǎi)了一張票。
“還有三位乘客沒(méi)有買(mǎi)票,請買(mǎi)票!”
……
“這售票員夠厲害,記得這么清楚,上來(lái)幾個(gè)人都記得?!毙〔烁袊@道。
“這也是業(yè)務(wù)能力的體現呀?!贝篪B(niǎo)解釋說(shuō)。
……
“先生請買(mǎi)票!”售票員對著(zhù)一位老外說(shuō)道。
“Sorry,What do you say?”老外看來(lái)不會(huì )中文。
“請買(mǎi)車(chē)票怎么說(shuō)?”售票員低聲的自言自語(yǔ)道,“Please buy……票怎么說(shuō)……”。
“ticket”小菜手掌放嘴邊,小聲地提醒了一句。
“謝謝,”售票員對小菜笑了笑,接著(zhù)用中國式英文對著(zhù)老外說(shuō)道,“Please buy ticket.”
“Oh!yes.”老外急忙掏錢(qián)包拿了一張十元人民幣。
“買(mǎi)票了,買(mǎi)票了,還有兩位,不要給不買(mǎi)票的人任何機會(huì )……”售票員找了老外錢(qián)后吆喝著(zhù),又對著(zhù)一穿著(zhù)同樣公交制服的女的說(shuō)道,“小姐,請買(mǎi)票!”
“我也是公交公司的,”這女的拿出一個(gè)公交證件,在售票員面前晃了晃。
“不好意思,公司早就出規定了,工作證不得作為乘車(chē)憑證?!笔燮眴T說(shuō)道。
“我乘車(chē)從來(lái)就沒(méi)有買(mǎi)過(guò)票,憑什么在這就要買(mǎi)票?!边@個(gè)乘客開(kāi)始耍賴(lài)。
此時(shí)旁邊的乘客都來(lái)勁了,七嘴八舌說(shuō)起來(lái)。
“公交公司的員工就不是乘客呀,國家總理來(lái)也要買(mǎi)票的?!?/span>
“這人怎么這樣,想占大伙的便宜呀?!?/span>
“你還當過(guò)去呀,現在二十一世紀不吃大鍋飯了。欠債還錢(qián),乘車(chē)買(mǎi)票,天經(jīng)地義……”
“行了行了,不就是一張票嗎,搞什么搞,吶!買(mǎi)票?!边@不想買(mǎi)票的小姐終于扛不住了,遞出去兩元錢(qián)買(mǎi)了票。
“還有哪一位沒(méi)有買(mǎi)票,請買(mǎi)票?!笔燮眴T繼續在擁擠的車(chē)廂里跋涉著(zhù)。
“小偷!你這小偷,把手機還我?!蓖蝗徽驹谛〔瞬贿h處的一個(gè)小姑娘對著(zhù)一個(gè)猥瑣的男人叫了起來(lái)。
“你不要亂講,我哪有偷你的手機?!?/span>
“我看見(jiàn)你剛才把手伸進(jìn)了我的包里。就是你偷的?!?/span>
“我沒(méi)有偷,你看錯了?!?/span>
“我明明看見(jiàn)你偷的?!毙」媚锛钡目蘖顺鰜?lái)。
小菜看不過(guò)去了,“你的手機號多少,我幫你打打看?!?/span>
“138****8888”小姑娘像是看到了希望。
“哇,這么強的號,手機一定不會(huì )丟?!毙〔肆w慕著(zhù),用自己的手機撥了這個(gè)號碼。
那人眼看著(zhù)不對,想往門(mén)口跑,小菜和大鳥(niǎo)沖了上去,一把按住他。
“你看,我的手機響了,就在他身上?!毙」媚锝辛似饋?lái),“就是他,他就是小偷?!?/span>
此時(shí)兩個(gè)小伙已經(jīng)把猥瑣男死死按在地板上。
“快打110報警!”大鳥(niǎo)喊道。
此時(shí)公交車(chē)也停了下來(lái),所有的乘客都議論著(zhù)“小偷真可惡”的話(huà)題。
不一會(huì ),民警來(lái)了,問(wèn)清楚了來(lái)由,正準備將小偷帶走時(shí),售票員對著(zhù)小偷發(fā)話(huà)了:“慢著(zhù),你是哪個(gè)沒(méi)有買(mǎi)票的人吧?”
“???嗯!是的?!毙⊥狄荒樉趩驶卮鸬?。
“想走?!可以。先買(mǎi)票再說(shuō)!”售票員干脆地說(shuō)。
小菜和大鳥(niǎo)對望一眼,異口同聲道“強!”
……
場(chǎng)景2:
人物:大鳥(niǎo)、小菜
“小菜,今天你真見(jiàn)到強人了吧?!贝篪B(niǎo)在下車(chē)后,對小菜說(shuō)道。
“這個(gè)售票員,實(shí)在夠強,”小菜學(xué)著(zhù)模仿道,“想走?!可以。先買(mǎi)票再說(shuō)!”
“這售票員其實(shí)在做一件重要的事,就是把車(chē)廂里的所有人都遍歷一遍,不放過(guò)一個(gè)不買(mǎi)票的乘客。這也是一個(gè)設計模式的體現?!?/span>
“大鳥(niǎo),你也夠強,什么都可以往設計模式上套,這也是模式?”
“當然是模式。這個(gè)模式就叫做迭代器模式?!?/span>
迭代器模式(Iterator),提供一種方法順序訪(fǎng)問(wèn)一個(gè)聚合對象中各個(gè)元素,而又不暴露該對象的內部表示。
“你想呀,售票員才不管你上來(lái)是人還是物(行李),不管是中國人還是外國人,不管是不是內部員工,甚至哪怕是馬上要抓走的小偷,只要是來(lái)乘車(chē)的乘客,就必須要買(mǎi)票。同樣道理,當你需要一個(gè)聚合對象,而且不管這些對象是什么都需要遍歷的時(shí)候,你就應該考慮用迭代器模式。另外,售票員可以從車(chē)頭到車(chē)尾來(lái)售票,也可以從車(chē)尾向車(chē)頭來(lái)售票,也就是說(shuō),你需要對聚合有多種方式遍歷時(shí),可以考慮用迭代器模式。由于不管乘客是什么,售票員的做法始終是相同的,都是從第一個(gè)開(kāi)始,下一個(gè)是誰(shuí),是否結束,當前售到哪個(gè)人了,這些方法每天她都在做,也就是說(shuō),為遍歷不同的聚合結構提供如開(kāi)始、下一個(gè)、是否結束、當前哪一項等統一的接口。”
“聽(tīng)你這么一說(shuō),好像這個(gè)模式也不簡(jiǎn)單哦?!?/span>
“哈,本來(lái)這個(gè)模式還是有點(diǎn)意思的,不過(guò)現如今看迭代器模式實(shí)用價(jià)值遠不如學(xué)習價(jià)值大了,Martin Fowler甚至在自己的網(wǎng)站上提出撤銷(xiāo)此模式。因為現在高級編程語(yǔ)言如C#、JAVA等本身已經(jīng)把這個(gè)模式做在語(yǔ)言中了?!?/span>
Martin Fowler是國際著(zhù)名的面向對象分析設計、UML、模式等方面的專(zhuān)家,敏捷開(kāi)發(fā)方法的創(chuàng )始人之一,他改變了人類(lèi)開(kāi)發(fā)軟件的模式,他被開(kāi)發(fā)者們尊稱(chēng)為“教父”?,F為ThoughtWorks公司的首席科學(xué)家。
“哦,是什么?”
“哈,foreachin 你熟悉嗎?”
“啊,原來(lái)是它,沒(méi)錯沒(méi)錯,它就是不需要知道集合對象是什么,就可以遍歷所有對象的循環(huán)工具,非常好用?!?/span>
“另外還有像IEnumerable接口也是為迭代器模式而準備的。不管如何,學(xué)習一下GOF的迭代器模式的基本結構,還是很有學(xué)習價(jià)值的。研究歷史是為了更好地迎接未來(lái)。”
《DesignPatterns: Elements of Reusable Object-Oriented Software》(即后述《設計模式》一書(shū)),由ErichGamma、Richard Helm、RalphJohnson和John Vlissides合著(zhù)(Addison-Wesley,1995)。這幾位作者常被稱(chēng)為“四人組(Gangof Four,GOF)”。
迭代器實(shí)現
代碼見(jiàn):Iterator _Level1
人物:大鳥(niǎo)、小菜
“看到?jīng)]有,這就是我們的優(yōu)秀售票員售票——迭代器的整個(gè)運作模式?!?/span>
“大鳥(niǎo),你說(shuō)為什么要有具體的迭代器ConcreteIterator來(lái)實(shí)現抽象的Iterator呢?我感覺(jué)這里不需要抽象呀,直接訪(fǎng)問(wèn)ConcreteIterator不是更好嗎?”
“哈,那是因為剛才有一個(gè)迭代器的好處你沒(méi)有注意,當你需要對聚集有多種方式遍歷時(shí),可以考慮用迭代器模式,事實(shí)上,售票員一定要從車(chē)頭到車(chē)尾這樣售票嗎?”
“你意思是,她可以從后向前遍歷?”
“當然可以,你不妨在寫(xiě)一個(gè)實(shí)現從后往前的具體迭代器類(lèi)看看?!?/span>
代碼見(jiàn):Iterator _Level2
.NET的迭代器實(shí)現
“剛才我們也說(shuō)過(guò),實(shí)際使用當中是不需要這么麻煩的,因為.NET框架已經(jīng)為你準備好了相關(guān)接口,你只需要去實(shí)現就好?!?/span>
IEnumerable/ IEnumerable<T>用于創(chuàng )建枚舉類(lèi)型,相當于迭代器模式的Aggregate(抽象類(lèi))。
public interface IEnumerable
{
// 返回一個(gè)循環(huán)訪(fǎng)問(wèn)集合的枚舉數
IEnumerator GetEnumerator();
}
public interface IEnumerable<T> : IEnumerable
{
// 返回一個(gè)循環(huán)訪(fǎng)問(wèn)集合的枚舉數
IEnumerator<T> GetEnumerator();
}
注意:
1.泛型IEnumerable<T>接口與IEnumerable的非泛型版本很相似。泛型版本從IEnumerable繼承,所以也必須實(shí)現IEnumerable接口。
2.與IEnumerable差不多,泛型版本也包含了一個(gè)方法GetEnumerator。然而,這個(gè)版本的GetEnumerator實(shí)現泛型IEnumerator<T>接口的類(lèi)對象。由于類(lèi)必須實(shí)現兩個(gè)GetEnumerator方法,我們需要顯式實(shí)現非泛型版本,并在類(lèi)中實(shí)現泛型版本。
3.一個(gè)Collection要支持foreach方式的遍歷,必須實(shí)現IEnumerable接口(亦即,必須以某種方式返回IEnumerator object)。
4.IEnumerator object具體實(shí)現了Iterator(通過(guò)MoveNext(),Reset(),Current)。
5.從這兩個(gè)接口的用詞選擇上,也可以看出其不同:IEnumerable是一個(gè)聲明式接口,聲明實(shí)現接口的class是“可枚舉(Enumerable)”的,但并沒(méi)有說(shuō)明如何實(shí)現枚舉器(Iterator);IEnumerator是一個(gè)實(shí)現式接口,IEnumerator object就是一個(gè)Iterator。
6.IEnumerator和IEnumerable通過(guò)IEnumerable的GetEnumerator()方法建立了鏈接,Client可以通過(guò)IEnumerable的GetEnumerator()得到IEnumerator object,在這個(gè)意義上,將GetEnumerator()看作IEnumerator object的FactoryMethod也未嘗不可。
IEnumerator/ IEnumerator<T>枚舉數。相當于迭代器模式的Iterator(抽象類(lèi))。
。
public interface IEnumerator
{
// 返回序列中當前位置項的屬性
object Current { get;}
// 將枚舉數推進(jìn)到集合下一個(gè)元素。
//方法返回值true表示迭代器成功前進(jìn)到集合中的下一個(gè)元素,
//返回false表示已經(jīng)位于集合的末尾。
bool MoveNext();
//恢復初始化指向的位置,該位置位于集合中第一個(gè)元素之前
void Reset();
}
public interface IEnumerator<T>:IEnumerator,IDisposable
{
// 獲取集合中的當前元素
T Current { get; }
}
注意:
1.IEnumerator是所有枚舉數的基接口。IEnumerator<T>接口使用泛型來(lái)返回實(shí)際的類(lèi)型,而不是object類(lèi)型對象。
2.由于IEnumerator和IEnumerator<T>都有一個(gè)叫做Current的成員,我們應該顯示實(shí)現IEnumerator版本,然后在類(lèi)中實(shí)現泛型版本。
3.枚舉數只允許讀取集合中的數據。枚舉數無(wú)法用于修改基礎集合。這也是為什么說(shuō)“不要在foreach循環(huán)中修改元素的原因”。
4.最初,枚舉數被定位于集合中第一個(gè)元素的前面。Reset也將枚舉數返回到此位置。在此位置,調用Current會(huì )引發(fā)異常。因此,在讀取Current的值之前,必須調用MoveNext將枚舉數提前到集合的第一個(gè)元素。
5.在調用MoveNext或Reset之前,Current返回同一對象。MoveNext將Current設置為下一個(gè)元素。
6.在傳遞到集合的末尾之后,枚舉數放在集合中最后一個(gè)元素后面,且調用MoveNext會(huì )返回False。如果最后一次調用MoveNext返回False,則調用Current會(huì )引發(fā)異常。若要再次將Current設置為集合的第一個(gè)元素,可以調用Reset,然后再調用MoveNext。
7.只要集合保持不變,枚舉數就將保持有效。如果對集合進(jìn)行了更改(如添加、修改或刪除元素),則該枚舉數將失效且不可恢復,并且下一次對MoveNext或Reset的調用將引發(fā)InvalidOperationExpression。如果在MoveNext和Current之間修改集合,那么即使枚舉數已經(jīng)無(wú)效,Current也將返回它所設置成的元素。
8.枚舉數沒(méi)有對集合的獨占訪(fǎng)問(wèn)權;因此,枚舉一個(gè)集合在本質(zhì)上不是一個(gè)線(xiàn)程安全的過(guò)程。甚至在對集合進(jìn)行同步處理時(shí),其它線(xiàn)程仍可以修改該集合,這會(huì )導致枚舉數引發(fā)異常。若要在枚舉過(guò)程中保證線(xiàn)程安全,可以在整個(gè)枚舉過(guò)程中鎖定集合,或者捕獲由于其它線(xiàn)程進(jìn)行的更改而引發(fā)的異常。
我們已經(jīng)知道如何使用IEnumerable和IEnumerator接口來(lái)創(chuàng )建可枚舉類(lèi)型和枚舉數,但是這種方法有幾個(gè)缺點(diǎn)。
首先,由Current返回的對象是object類(lèi)型的。對于值類(lèi)型而言,在由current返回之前必須裝箱成object。在從Current獲取之后,又必須再一次拆箱。如果需要操作大量的數據,會(huì )帶來(lái)嚴重的性能問(wèn)題。
非泛型接口方法的另外一個(gè)缺點(diǎn)是失去了類(lèi)型安全。值被作為對象來(lái)枚舉,所以可以是任何類(lèi)型。這就排除了編譯時(shí)的類(lèi)型檢測。
代碼見(jiàn):ForeachSample02 – 非泛型枚舉數
代碼見(jiàn):ForeachSample03 – 泛型枚舉數
“你會(huì )發(fā)現,這兩個(gè)接口,特別是IEnumerator要比我們剛才寫(xiě)的抽象類(lèi)Iterator要簡(jiǎn)潔,但可實(shí)現的功能卻一點(diǎn)不少,這其實(shí)也是對GOF的設計改良的結果?!?/span>
“其實(shí)具體類(lèi)實(shí)現這兩個(gè)接口的代碼也差別不大,是嗎?”
“是的,區別不大,另外這兩個(gè)接口還有相應的泛型接口?!?/span>
“有了這個(gè)基礎,你再來(lái)看你最熟悉的foreach in 就很簡(jiǎn)單了”。
代碼見(jiàn):Iterator _Level3
“這里用到了foreachin 而在編譯器里做了寫(xiě)什么呢?其實(shí)它做的是下面的工作?!?/span>
IEnumerator<string> e = lst.GetEnumerator();
e.Reset();
while(e.MoveNext())
{
Console.WriteLine("{0}請買(mǎi)票!", e.Current);
}
“原來(lái)foreachin 就是實(shí)現這兩個(gè)接口來(lái)實(shí)現循環(huán)遍歷呀?!?/span>
“是的,盡管我們不需要顯示的引用迭代器,但系統本身還是通過(guò)迭代器來(lái)實(shí)現遍歷的。總地來(lái)說(shuō),迭代器(Iterator)模式就是分離了集合對象的遍歷行為,抽象出一個(gè)迭代器類(lèi)來(lái)負責,這樣即可以做到不暴露集合的內部結構,又可讓外部代碼透明地訪(fǎng)問(wèn)集合內部的數據。迭代器模式在訪(fǎng)問(wèn)數組、集合、列表等數據時(shí),尤其是數據庫數據操作時(shí),有非常普遍的應用,但由于它太普遍了,所以各種高級語(yǔ)言都對它進(jìn)行了封裝,所以反而給人感覺(jué)此模式本身不太常用了?!?/span>
通過(guò)微信學(xué)習的知識只能是碎片化的知識,作為新時(shí)代的我們希望能夠構建自己的知識結構,使我們的知識體系化,系統化,以后在遇到碎片化的知識,我們做的只是融合到自己的知識結構中,故我們將推出“與LSGO一起學(xué)”系列課程,幫助大家來(lái)構建知識框架,初步規劃有:
“與LSGO一起學(xué)C++”;
“與LSGO一起學(xué)C#”;
“與LSGO一起學(xué)Matlab”;
“與LSGO一起學(xué)數據結構”;
“與LSGO一起學(xué)設計模式”;
“與LSGO一起學(xué)可視化建模語(yǔ)言(UML)”;
“與LSGO一起學(xué)線(xiàn)性代數”;
“與LSGO一起學(xué)高等數學(xué)”
“與LSGO一起學(xué)概率論與數理統計”;
“與LSGO一起學(xué)抽象代數;
“與LSGO一起學(xué)點(diǎn)集拓撲”
“與LSGO一起學(xué)數字圖像處理”;
“與LSGO一起學(xué)智能計算”;
如果對這些內容感興趣,可以一起來(lái)學(xué)習討論。
我們的官網(wǎng): www.lsgogroup.com
聯(lián)系客服