設(shè)計(jì)模式學(xué)習(xí)專(zhuān)欄九--------迭代器模式
名稱(chēng) : 迭代器模式(Iterator)
價(jià)值觀念: 管理良好的集合
場(chǎng)景
爆炸性新聞: 對(duì)象村餐廳和煎餅屋餐廳屋合并了 , 我們打算用煎餅屋的菜單當(dāng)早餐 , 用對(duì)象村餐廳的菜單當(dāng)中餐.
可是 ...
煎餅屋的菜單用集合來(lái)存儲(chǔ) , 對(duì)象村餐廳使用數(shù)組來(lái)存儲(chǔ)... 它們都不想改變自己的存儲(chǔ)結(jié)構(gòu), 因?yàn)橛斜旧砗芏啻a都依賴(lài)于原有的數(shù)據(jù)結(jié)構(gòu)
那么, 合并后的餐廳的菜單打印功能怎么實(shí)現(xiàn)呢?
女招待的代碼如下
public void printMenu2() {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
ArrayList<MenuItem> breakfastItems =pancakeHouseMenu.getMenuItems();
DinerMenu dinerMenu = new DinerMenu();
MenuItem[] lunchItems = dinerMenu.getMenuItems();
for (int i=0;i<breakfastItems.size();i++){
MenuItem menuItem = breakfastItems.get(i);
System.out.println(menuItem.getName()+" ");
System.out.println(menuItem.getPrice()+" ");
System.out.println(menuItem.getDescription()+" ");
}
for (int i=0;i<lunchItems.length;i++){
MenuItem menuItem = lunchItems[i];
System.out.println(menuItem.getName()+" ");
System.out.println(menuItem.getPrice()+" ");
System.out.println(menuItem.getDescription()+" ");
}
}
- 出現(xiàn)的問(wèn)題 :
- 女招待需要知道如何遍歷每個(gè)餐廳的菜單 , 也就是要認(rèn)識(shí)它們內(nèi)部的數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)(數(shù)組,ArrayList,或者鏈表,堆)
- 并且每次加入一個(gè)菜單 , 就要多寫(xiě)一重for循環(huán) .
- 即女招待 和 集合遍歷的具體實(shí)現(xiàn)耦合到了一起 (getMenuItems 方法返回值可能各不相同)
- 我們?nèi)绻修k法找出一個(gè)方法,讓它們
獲取菜單
實(shí)現(xiàn)一個(gè)相同的接口 ,那該有多好 , 這樣一來(lái),我們就可以最小化女招待代碼中的具體引用, 同時(shí)還有希望擺脫遍歷這兩個(gè)菜單所需要的多個(gè)循環(huán)
如何解決
- 變化部分 : 由于每個(gè)菜單的具體存儲(chǔ)實(shí)現(xiàn)可能不同 , 因此遍歷的過(guò)程可能會(huì)有很大差異
嘗試封裝遍歷的部分
我們做了什么
- 我們對(duì)遍歷進(jìn)行了封裝
- 使用hasNext()方法告訴我們,是否在這個(gè)聚合中還有更多的元素
- 使用next()方法返回這個(gè)聚合中的下一個(gè)對(duì)象
- 有了這個(gè)接口, 就可以為各種對(duì)象集合實(shí)現(xiàn)迭代器了
- 對(duì)于客戶(hù)來(lái)說(shuō) , 不再關(guān)注聚合遍歷的具體實(shí)現(xiàn), 只需要拿到該聚合對(duì)應(yīng)的迭代器 , 就能輕松進(jìn)行遍歷了,將客戶(hù)與遍歷具體實(shí)現(xiàn)進(jìn)行了解耦
迭代器模式總覽
定義:提供一種方法順序訪問(wèn)一個(gè)聚合對(duì)象中的各個(gè)元素,而又不暴露其內(nèi)部的表示
(即封裝遍歷
, 將遍歷元素的動(dòng)作抽取出來(lái), 將女招待解耦了
.女招待無(wú)需知道菜單列表是如何被實(shí)現(xiàn)的)
-
類(lèi)圖
-
模式的理解
- 角色
- 聚合(集合)
- 迭代器
- 客戶(hù)
- 細(xì)節(jié)
- 迭代器模式讓我們能夠游走于聚合內(nèi)的每一個(gè)元素,而又不暴露其內(nèi)部表示
- 把游走的任務(wù)放到迭代器上,而不是聚合上 , 這樣簡(jiǎn)化了聚合的接口和實(shí)現(xiàn), 也讓責(zé)任各得其所
- 角色
-
為什么要使用迭代器 ? 直接把遍歷部分放到聚合中不就好了嗎?
單一責(zé)任原則 : 一個(gè)類(lèi)應(yīng)該只有一個(gè)引起變化的原因
類(lèi)的每個(gè)責(zé)任都有改變的潛在區(qū)域,如果一個(gè)類(lèi)超過(guò)一個(gè)責(zé)任,意味著超過(guò)一個(gè)改變的區(qū)域.
如何把遍歷部分放到聚合中, 集合就有了2個(gè)責(zé)任
①確定好數(shù)據(jù)結(jié)構(gòu) , 管理好自己的聚合
②管理好遍歷的過(guò)程
那么這個(gè)類(lèi)可能需要發(fā)生改變的幾率就會(huì)變高
? 并且,這樣的話外面的客戶(hù)就不能自己控制遍歷過(guò)程了
案例代碼部分
-
對(duì)象村對(duì)應(yīng)的迭代器
public class DinerMenuIterator implements Iterator<MenuItem> { MenuItem[] list; int position = 0; public DinerMenuIterator(MenuItem[] list) { this.list = list; } public MenuItem next() { MenuItem menuItem = list[position]; position = position + 1; return menuItem; } public boolean hasNext() { if (position >= list.length || list[position] == null) { return false; } else { return true; } } public void remove() { if (position <= 0) { throw new IllegalStateException ("You can't remove an item until you've done at least one next()"); } if (list[position-1] != null) { for (int i = position-1; i < (list.length-1); i++) { list[i] = list[i+1]; } list[list.length-1] = null; } } }
-
煎餅屋的代碼
public class PancakeHouseMenu implements Menu { ArrayList<MenuItem> menuItems; ... public Iterator<MenuItem> createIterator() { return menuItems.iterator(); } }
-
女招待的代碼
public class Waitress { Menu pancakeHouseMenu; Menu dinerMenu; public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) { this.pancakeHouseMenu = pancakeHouseMenu; this.dinerMenu = dinerMenu; } public void printMenu() { Iterator<MenuItem> pancakeIterator = pancakeHouseMenu.createIterator(); Iterator<MenuItem> dinerIterator = dinerMenu.createIterator(); System.out.println("MENU\n----\nBREAKFAST"); printMenu(pancakeIterator); System.out.println("\nLUNCH"); printMenu(dinerIterator); } private void printMenu(Iterator<MenuItem> iterator) { while (iterator.hasNext()) { MenuItem menuItem = iterator.next(); System.out.print(menuItem.getName() + ", "); System.out.print(menuItem.getPrice() + " -- "); System.out.println(menuItem.getDescription()); } } }
參考
? 書(shū)籍: HeadFirst設(shè)計(jì)模式
? 代碼參考地址: 我就是那個(gè)地址