迭代器模式(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