Java設計模式(十六)之行為型模式:迭代子模式
一、定義:
迭代器模式,就是提供一種方法訪問一個集合物件中的各個元素,而不暴露其內部的表示。在實際的開發過程中,我們可能需要針對不同的需求,可能需要以不同的方式來遍歷整個整合物件,但是我們不希望在聚合物件的抽象介面層中充斥著各種不同的遍歷操作。這個時候我們就需要這樣一種東西,它應該具備如下三個功能:
(1)能夠遍歷一個集合物件。(2)我們不需要了解聚合物件的內部結構。(3)能夠提供多種不同的遍歷方式。
這三個功能就是迭代器模式需要解決的問題。作為一個功能強大的模式,迭代器模式把在元素之間遊走的責任交給迭代器,而不是集合物件。這樣做就簡化了聚合的介面和實現,也可以讓聚合更專注在它所應該專注的事情上,這樣做就更加符合單一責任原則。
UML結構圖:
從上面可以看書迭代器模式有如下幾個角色:
(1)Iterator: 抽象迭代器:所有迭代器都需要實現的介面,提供了遊走聚合物件元素之間的方法。
(2)ConcreteIterator: 具體迭代器。利用這個具體的迭代器能夠對具體的聚合物件進行遍歷。每一個聚合物件都應該對應一個具體的迭代器。
(3)Aggregate: 抽象聚合類。
(4)ConcreteAggregate: 具體聚合類。實現creatorIterator()方法,返回該聚合物件的迭代器。
二 、模式實現:
你專案組接到一個專案:對電視機的電視訊道、電影和收音機選單進行統一管理,建立一個統一的選單管理介面,能夠看到所有的電視介面、電影介面和收音機頻道。你有三個手下:小李子、小杏子、小安子,他們分別就每個模組做開發工作,看他們都做了哪些工作。
UML結構圖:
首先我們需要定義迭代器介面。Iterator.java
public interface Iterator {
boolean hasNext();
Object next();
}
然後是我們兩個具體的迭代器。一個迭代器遍歷電視介面、一個迭代器遍歷電影介面:
//電影節目的迭代器 public class FilmMenuIterator implements Iterator{ MenuItem[] menuItems; int position = 0; public FilmMenuIterator(MenuItem[] menuItems){ this.menuItems = menuItems; } public boolean hasNext() { if(position > menuItems.length-1 || menuItems[position] == null){ return false; } return true; } public Object next() { MenuItem menuItem = menuItems[position]; position ++; return menuItem; } }
//電視介面的迭代器
public class TVChanneMenuIterator implements Iterator{
List<MenuItem> menuItems;
int position = 0;
public TVChanneMenuIterator(List<MenuItem> menuItems){
this.menuItems = menuItems;
}
public boolean hasNext() {
if(position > menuItems.size()-1 || menuItems.get(position) == null){
return false;
}
else{
return true;
}
}
public Object next() {
MenuItem menuItem = menuItems.get(position);
position ++;
return menuItem;
}
}
然後是選單介面,該介面提供返回具體迭代器的方法:createIterator()。
public interface TelevisionMenu {
public void addItem(int channe,String name,String description);
public Iterator createIrerator();
}
兩個具體聚合類。這個兩個聚合類實現createIterator()方法,分別返回電視介面的聚合類和電影介面的聚合類。
public class FilmMenu implements TelevisionMenu{
static final int MAX_ITEMS = 5; //選單最大長度
MenuItem[] menuItems;
int numberOfItems = 0;
/**
* 建構函式完成初始化
*/
public FilmMenu(){
menuItems = new MenuItem[MAX_ITEMS];
addItem(1, "絕世天劫", "這是布魯斯威利斯主演的...");
addItem(2, "達芬奇密碼", "這是我最喜歡的電影之一...");
addItem(3, "黑客帝國123", "不知道你能夠看懂不???");
addItem(4, "我的女友是機器人", "一部我不反感的經典愛情電影...");
addItem(5, "肖申克的救贖", "自由,幸福,離你有多遠");
}
/**
* @desc 將電影解決新增到選單項中
* @param channe
* @param name
* @param description
* @return void
*/
public void addItem(int channe,String name,String description){
MenuItem tvmenuiItem = new MenuItem(channe, name, description);
//判斷陣列是否越界
if(numberOfItems > MAX_ITEMS){
System.out.println("不好意思,選單滿了....");
}
else{
menuItems[numberOfItems] = tvmenuiItem;
numberOfItems ++;
}
}
public Iterator createIrerator() {
return new FilmMenuIterator(menuItems);
}
}
public class TVChanneMenu implements TelevisionMenu{
List<MenuItem> menuItems;
/**
* 建構函式完成初始化
*/
public TVChanneMenu(){
menuItems = new ArrayList<MenuItem>();
addItem(1, "CCTV-1", "This is CCTV-1");
addItem(2, "CCTV-2", "This is CCTV-2");
addItem(3, "CCTV-3", "This is CCTV-3");
addItem(4, "CCTV-4", "This is CCTV-4");
addItem(5, "CCTV-5", "This is CCTV-5");
}
/**
* @desc 將電視訊道節目新增選單集合中
* @param channe 頻道
* @param name 名稱
* @param description 描述
* @return void
*/
public void addItem(int channe,String name,String description){
MenuItem tvMenuItem = new MenuItem(channe, name, description);
menuItems.add(tvMenuItem);
}
public Iterator createIrerator() {
return new TVChanneMenuIterator(menuItems);
}
}
終於完成了,現在就可以來實現主選單了,用來展示、遍歷所有的電視、電影介面咯。
public class MainMenu {
TelevisionMenu tvMenu;
FilmMenu filmMenu;
public MainMenu(TelevisionMenu tvMenu,FilmMenu filmMenu){
this.tvMenu = tvMenu;
this.filmMenu = filmMenu;
}
public void printMenu(){
Iterator tvIterator = tvMenu.createIrerator();
Iterator filmIterator = filmMenu.createIrerator();
System.out.println("電視節目有:");
printMenu(tvIterator);
System.out.println("----------------------------------------------------------------");
System.out.println("電影節目有:");
printMenu(filmIterator);
}
private void printMenu(Iterator iterator) {
while(iterator.hasNext()){
MenuItem menuItem = (MenuItem) iterator.next();
System.out.print("channe:"+menuItem.getChanne()+", ");
System.out.print("name:"+menuItem.getName()+", ");
System.out.println("description :"+menuItem.getDescription());
}
}
}
測試程式:
public class Test {
public static void main(String[] args) {
TVChanneMenu tvMenu = new TVChanneMenu();
FilmMenu filmMenu = new FilmMenu();
MainMenu mainMenu = new MainMenu(tvMenu, filmMenu);
mainMenu.printMenu();
}
}
執行結果:
三、迭代子模式小結:
1、優點:
(1)迭代子模式簡化了集合的介面。迭代子具備了一個遍歷介面,這樣集合的介面就不必具備遍歷介面。
(2)每一個聚集物件都可以有一個或多個迭代子物件,每一個迭代子的迭代狀態可以是彼此獨立的。因此,一個聚集物件可以同時有幾個迭代在進行之中。
(3)由於遍歷演算法被封裝在迭代子角色裡面,因此迭代的演算法可以獨立於聚集角色變化。
(4).更好的封裝性,訪問一個集合物件的元素,無需暴露容器內部表示。
2、缺點:
(1)由於迭代器模式將儲存資料和遍歷資料的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。
(2)對於比較簡單的遍歷,使用迭代器模式顯得較為繁瑣,比如ArrayList直接就可以用for迴圈+get方法來遍歷;
(3)抽象迭代器的設計難度較大,需要充分考慮到系統將來的擴充套件,例如JDK內建迭代器Iterator就無法實現逆向遍歷,如果需要實現逆向遍歷,只能通過其子類ListIterator等來實現,而ListIterator迭代器無法用於操作Set型別的聚合物件。在自定義迭代器時,建立一個考慮全面的抽象迭代器並不是件很容易的事情。
3、適用場景:
(1)訪問一個集合物件的內容而無須暴露它的內部表示。
(2)需要為集合物件提供多種遍歷方式。
(3)為遍歷不同的聚合結構提供一個統一的介面。
PS:由於容器與迭代器的關係太密切了,所以大多數語言在實現容器的時候都給提供了迭代器,並且這些語言提供的容器和迭代器在絕大多數情況下就可以滿足我們的需要,所以現在需要我們自己去實踐迭代器模式的場景還是比較少見的,我們只需要使用語言中已有的容器和迭代器就可以了。
原部落格連結: