迭代器模式(Iterator): 迭代器模式允許你訪問一個數據項序列中的所有元素,而無須關心序列是什麼類型(數組、鏈表、列表或任何其他類型)。它能有效地構建一個數據管道,經過一系列不同的轉換或過濾後再從管道的另一端出來。迭代器模式就是提供一種遍歷集合元素的統一介面,用一致的方法遍歷集合元素,不需要知 ...
迭代器模式(Iterator):
迭代器模式允許你訪問一個數據項序列中的所有元素,而無須關心序列是什麼類型(數組、鏈表、列表或任何其他類型)。它能有效地構建一個數據管道,經過一系列不同的轉換或過濾後再從管道的另一端出來。迭代器模式就是提供一種遍歷集合元素的統一介面,用一致的方法遍歷集合元素,不需要知道集合對象的底層表示。
迭代器模式的角色:
1)抽象迭代器(Iterator):介面聲明瞭遍歷集合所需的操作(獲取下一個元素、獲取當前位置和重新開始迭代等)。
2)具體迭代器(ConcreteIterator):實現遍歷集合的一種特定演算法。迭代器對象必須跟蹤自身遍歷的進度。這使得多個迭代器可以相互獨立地遍歷同一個集合。
3)抽象聚合(Aggregate):介面聲明一個或多個方法來獲取與集合相容的迭代器。返回方法的類型必須被聲明為迭代器介面。
4)具體聚合(ConcreteAggregate):會在客戶端請求迭代器時返回一個特定的具體迭代器類實體。
5)客戶端(Client):通過集合和迭代器的介面與兩者進行交互。 這樣一來客戶端無需與具體類進行耦合, 允許同一客戶端代碼使用各種不同的集合和迭代器。
示例:
先假設有兩家餐廳,主營業務不同,一家是早餐店,一家是晚餐店。
1 /// <summary> 2 /// 菜單明細項 3 /// </summary> 4 public class MenuItem 5 { 6 private string name; 7 private string description; 8 private bool vegetarin; 9 private double price; 10 11 public MenuItem(string name, string description, bool vegetarin, double price) 12 { 13 this.name = name; 14 this.description = description; 15 this.vegetarin = vegetarin; 16 this.price = price; 17 } 18 19 public string GetName() 20 { 21 return this.name; 22 } 23 24 public double GetPrice() 25 { 26 return price; 27 } 28 29 public bool IsVegetarian() 30 { 31 return vegetarin; 32 } 33 34 public string GetDescription() 35 { 36 return description; 37 } 38 } 39 40 /// <summary> 41 /// 早餐菜單 42 /// </summary> 43 public class BreakfastMenu 44 { 45 private List<MenuItem> menuItems; 46 47 public BreakfastMenu() 48 { 49 menuItems = new List<MenuItem>(); 50 AddItem("牛奶", "牛奶description", false, 3.0); 51 AddItem("油條", "油條description", false, 1.0); 52 AddItem("饅頭", "饅頭description", true, 1.0); 53 AddItem("豆漿", "DoujiangDescription", true, 1.5); 54 } 55 56 public void AddItem(string name, string description, bool vegetarian, double price) 57 { 58 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 59 menuItems.Add(menuItem); 60 } 61 62 public List<MenuItem> GetMenuItems() 63 { 64 return menuItems; 65 } 66 } 67 68 /// <summary> 69 /// 晚餐菜單 70 /// </summary> 71 public class DinnerMenu 72 { 73 private static readonly int Max_ITEMS = 6; 74 private int numberOfItems = 0; 75 private MenuItem[] menuItems; 76 77 public DinnerMenu() 78 { 79 menuItems = new MenuItem[Max_ITEMS]; 80 AddItem("香菇豆腐飯", "香菇豆腐", false, 10.5); 81 AddItem("蛋炒飯", "哈哈", false, 8.5); 82 AddItem("魚香肉絲", "你猜", true, 15.5); 83 } 84 85 public void AddItem(string name, string description, bool vegetarian, double price) 86 { 87 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 88 if (numberOfItems > Max_ITEMS) 89 { 90 Console.WriteLine("菜單已滿"); 91 } 92 else 93 { 94 menuItems[numberOfItems] = menuItem; 95 numberOfItems++; 96 } 97 } 98 99 public MenuItem[] GetMenuItems() 100 { 101 return menuItems; 102 } 103 }
現在兩家合併了,服務員那菜單的時候就要拿兩份菜單。
1 public static void Main(string[] args) 2 { 3 BreakfastMenu breakfastMenu = new BreakfastMenu(); 4 List<MenuItem> breakfastItems = breakfastMenu.GetMenuItems(); 5 6 DinnerMenu dinerMenu = new DinnerMenu(); 7 MenuItem[] lunchItems = dinerMenu.GetMenuItems(); 8 9 for (int i = 0; i < breakfastItems.Count; i++) 10 { 11 MenuItem menuItem = breakfastItems[i] as MenuItem; 12 Console.WriteLine(menuItem.GetName() + " " + menuItem.GetPrice().ToString() + " " + menuItem.GetDescription().ToString()); 13 } 14 15 for (int j = 0; j < lunchItems.Length; j++) 16 { 17 MenuItem lunchItem = lunchItems[j]; 18 if (lunchItem != null) 19 { 20 Console.WriteLine(lunchItem.GetName() + " " + lunchItem.GetPrice().ToString() + " " + lunchItem.GetDescription().ToString()); 21 } 22 } 23 }
我們發現,由於兩份菜單數據結構的不同,我們不得不重寫多餘的代碼,顯得很臃腫。我們會想:能不能有一個東西能夠讓我們不需要知道菜單的數據結構,直接就可以獲取其內部元素呢?答案是肯定的,這就是我們本節所說的迭代器模式。
先定義一個介面迭代器並實現晚餐菜單迭代器和晚餐菜單迭代器。
1 /// <summary> 2 /// 介面迭代器 3 /// </summary> 4 public interface Iterator 5 { 6 /// <summary> 7 /// 用來判斷下一個元素是否為空 8 /// </summary> 9 /// <returns></returns> 10 bool HasNext(); 11 12 /// <summary> 13 /// 用來獲取當前元素 14 /// </summary> 15 /// <returns></returns> 16 object Next(); 17 } 18 19 /// <summary> 20 /// 早餐菜單迭代器 21 /// </summary> 22 public class BreakfastIterator : Iterator 23 { 24 private List<MenuItem> items; 25 private int position = 0; 26 27 public BreakfastIterator(List<MenuItem> items) 28 { 29 this.items = items; 30 } 31 32 public bool HasNext() 33 { 34 return position <= items.Count - 1 && items[position] != null; 35 } 36 37 public object Next() 38 { 39 MenuItem item = items[position]; 40 position++; 41 return item; 42 } 43 } 44 45 /// <summary> 46 /// 晚餐菜單迭代器 47 /// </summary> 48 public class DinnerIterator : Iterator 49 { 50 private MenuItem[] items; 51 private int position = 0; 52 53 public DinnerIterator(MenuItem[] items) 54 { 55 this.items = items; 56 } 57 58 public bool HasNext() 59 { 60 return position <= items.Length && items[position] != null; 61 } 62 63 public object Next() 64 { 65 MenuItem item = items[position]; 66 position++; 67 return item; 68 } 69 }
修改菜單。
1 /// <summary> 2 /// 抽象聚合對象,用於創建一個迭代器對象 3 /// </summary> 4 public interface IMenu 5 { 6 Iterator CreateIterator(); 7 } 8 9 /// <summary> 10 /// 早餐菜單 11 /// </summary> 12 public class BreakfastMenu : IMenu 13 14 { 15 private List<MenuItem> menuItems; 16 17 public BreakfastMenu() 18 { 19 menuItems = new List<MenuItem>(); 20 AddItem("牛奶", "牛奶description", false, 3.0); 21 AddItem("油條", "油條description", false, 1.0); 22 AddItem("饅頭", "饅頭description", true, 1.0); 23 AddItem("豆漿", "DoujiangDescription", true, 1.5); 24 } 25 26 public void AddItem(string name, string description, bool vegetarian, double price) 27 { 28 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 29 menuItems.Add(menuItem); 30 } 31 32 //public List<MenuItem> GetMenuItems() 33 //{ 34 // return menuItems; 35 //} 36 37 public Iterator CreateIterator() 38 { 39 return new BreakfastIterator(menuItems); 40 } 41 } 42 43 /// <summary> 44 /// 晚餐菜單 45 /// </summary> 46 public class DinnerMenu : IMenu 47 48 { 49 private static readonly int Max_ITEMS = 6; 50 private int numberOfItems = 0; 51 private MenuItem[] menuItems; 52 53 public DinnerMenu() 54 { 55 menuItems = new MenuItem[Max_ITEMS]; 56 AddItem("香菇豆腐飯", "香菇豆腐", false, 10.5); 57 AddItem("蛋炒飯", "哈哈", false, 8.5); 58 AddItem("魚香肉絲", "你猜", true, 15.5); 59 } 60 61 public void AddItem(string name, string description, bool vegetarian, double price) 62 { 63 MenuItem menuItem = new MenuItem(name, description, vegetarian, price); 64 if (numberOfItems > Max_ITEMS) 65 { 66 Console.WriteLine("菜單已滿"); 67 } 68 else 69 { 70 menuItems[numberOfItems] = menuItem; 71 numberOfItems++; 72 } 73 } 74 75 //public MenuItem[] GetMenuItems() 76 //{ 77 // return menuItems; 78 //} 79 80 public Iterator CreateIterator() 81 { 82 return new DinnerIterator(menuItems); 83 } 84 }
這個時候,兩份餐單的輸出是這樣的。
1 public static void Main(string[] args) 2 { 3 IMenu breakfastMenu = new BreakfastMenu(); 4 IMenu dinnerMenu = new DinnerMenu(); 5 breakfastMenu.CreateIterator(); 6 Iterator dinnerIterator = dinnerMenu.CreateIterator(); 7 Iterator breakfastIterator = breakfastMenu.CreateIterator(); 8 9 Print(breakfastIterator); 10 Print(dinnerIterator); 11 12 static void Print(Iterator iterator) 13 { 14 while (iterator.HasNext()) 15 { 16 MenuItem menuItem = (MenuItem)iterator.Next(); 17 Console.WriteLine(menuItem.GetName() + " " + menuItem.GetPrice().ToString() + " " + menuItem.GetDescription().ToString()); 18 } 19 } 20 }
迭代器模式適用性:
1)當集合背後為複雜的數據結構,且你希望對客戶端隱藏其複雜性時(出於使用便利性或安全性的考慮),可以使用迭代器。
2)可以減少程式中重覆的遍歷代碼。
3)如果你希望代碼能夠遍歷不同的甚至是無法預知的數據結構,可以使用迭代器。
迭代器模式的優缺點:
優點:
1)它支持以不同的方式遍歷一個聚合對象。
2)迭代器簡化了聚合類。
3)在同一個聚合上可以有多個遍歷。
4)在迭代器模式中,增加新的聚合類和迭代器類都很方便,無須修改原有代碼。符合OCP原則。
缺點:由於迭代器模式將存儲數據和遍曆數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。
參考:https://www.cnblogs.com/lzhp/p/3427704.html