设计模式-行为型-迭代器模式
迭代器模式(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原则。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
参考: