迭代器模式(Iterator)定义:提供一种顺序访问聚合对象中各个元素的方法,而又不暴露其内部的表示。
实际上现在很多语言都自带迭代器,无需我们单独实现,比如c#中的foreach-in,但通过学习迭代器模式,我们可以看到类似foreach-in这种语法背后的逻辑,同时感受一下GOF所在的那个时代大神们的创造力。
c#中foreach-in是语言内置迭代器的语法糖,List、数组、Dictionary等等都可以使用foreach方便地遍历,这也正是迭代器的好处,被遍历的对象不需要暴露自己内部的表示,这样遍历的代码就可以和被遍历对象解耦。
类图如下:
示例代码使用Head Forst设计模式中的例子,煎饼屋的菜单PancakeHouseMenu使用List存放,餐厅的菜单DinerMenu使用数组存放,两者合并后,为了能够统一地展示他们的菜单项,使用迭代器模式处理
代码如下:
MenuItem规定了菜单的统一格式
1 class MenuItem 2 { 3 public MenuItem(string name, string description, bool isVegetarian, double price) 4 { 5 this.name = name; 6 this.description = description; 7 this.isVegetarian = isVegetarian; 8 this.price = price; 9 } 10 string name; 11 12 public string Name 13 { 14 get { return name; } 15 set { name = value; } 16 } 17 string description; 18 19 public string Description 20 { 21 get { return description; } 22 set { description = value; } 23 } 24 bool isVegetarian; 25 26 public bool IsVegetarian 27 { 28 get { return isVegetarian; } 29 set { isVegetarian = value; } 30 } 31 double price; 32 33 public double Price 34 { 35 get { return price; } 36 set { price = value; } 37 } 38 }
DinerMenu类
1 class DinerMenu 2 { 3 static readonly int MAX_ITEMS = 6; 4 int numberOfItems = 0; 5 MenuItem[] menuItem; 6 public DinerMenu() 7 { 8 menuItem = new MenuItem[MAX_ITEMS]; 9 AddItem("Vegetarian BLT", 10 "(Fakin') Bacon with lettuce&tomato on whole wheat", 11 true, 2.99); 12 AddItem("BLT", 13 "Bacon with lettuce&tomato on whole wheat", 14 false, 2.99); 15 AddItem("Soup of the Day", 16 "Soup of the Day, with a side of potato salad", 17 false, 3.29); 18 AddItem("HotDog", 19 "A hot dog, with saurkraut, relish, onions, topped with chesse", 20 false, 3.05); 21 22 } 23 24 public void AddItem(string name, string description, bool isVegetarian, double price) 25 { 26 MenuItem item = new MenuItem(name, description, isVegetarian, price); 27 if (numberOfItems >= MAX_ITEMS) 28 { 29 Console.WriteLine("Over Flow!!!"); 30 } 31 else 32 { 33 menuItem[numberOfItems] = item; 34 numberOfItems++; 35 } 36 } 37 38 public MenuItem[] GetMenuItems() 39 { 40 return menuItem; 41 } 42 43 public Iterator CreateIterator() 44 { 45 return new DinerMenuIterator(menuItem); 46 } 47 }
PancakeHouseMenu类
1 class PancakeHouseMenu 2 { 3 List<MenuItem> listMenu = new List<MenuItem>(); 4 public PancakeHouseMenu() 5 { 6 MenuItem item = new MenuItem( 7 "K&B's Pancake Breakfast", 8 "Pancakes with scrambled eggs, and toast", 9 true, 10 2.99); 11 listMenu.Add(item); 12 item = new MenuItem( 13 "Regular Pancake Breakfast", 14 "Pancakes with fired eggs, sausage", 15 false, 16 2.99); 17 listMenu.Add(item); 18 item = new MenuItem( 19 "Blueberry Pancake", 20 "Pancakes made with fresh blueberry", 21 true, 22 3.49); 23 listMenu.Add(item); 24 item = new MenuItem( 25 "Waffles", 26 "Waffles, with your choice of blueberries or strawberries", 27 true, 28 3.59); 29 listMenu.Add(item); 30 } 31 32 public void AddItem(string name, string description, bool isVegetarian, double price) 33 { 34 MenuItem item = new MenuItem(name, description, isVegetarian, price); 35 listMenu.Add(item); 36 } 37 public List<MenuItem> GetMenu() 38 { 39 return listMenu; 40 } 41 42 public Iterator CreateIterator() 43 { 44 return new PancakeHouseIterator(listMenu); 45 } 46 }
迭代器
1 interface Iterator 2 { 3 bool HaveNext(); 4 object Next(); 5 } 6 7 class DinerMenuIterator : Iterator 8 { 9 MenuItem[] items; 10 int position = 0; 11 public DinerMenuIterator(MenuItem[] items) 12 { 13 this.items = items; 14 } 15 16 public bool HaveNext() 17 { 18 if (position >= items.Length || items[position] == null) 19 { 20 return false; 21 } 22 else 23 { 24 return true; 25 } 26 } 27 28 public object Next() 29 { 30 31 MenuItem item = items[position]; 32 position++; 33 return item; 34 } 35 } 36 37 class PancakeHouseIterator : Iterator 38 { 39 List<MenuItem> items; 40 int position = 0; 41 public PancakeHouseIterator(List<MenuItem> items) 42 { 43 this.items = items; 44 } 45 46 public bool HaveNext() 47 { 48 if (position >= items.Count) 49 { 50 return false; 51 } 52 else 53 { 54 return true; 55 } 56 } 57 58 public object Next() 59 { 60 61 MenuItem item = items[position]; 62 position++; 63 return item; 64 } 65 }
Waitress,服务员,相当于迭代器的使用者
1 class Waitress 2 { 3 DinerMenu dinerMenu; 4 PancakeHouseMenu pancakeMenu; 5 public Waitress(DinerMenu dinerMenu, PancakeHouseMenu pancakeMenu) 6 { 7 this.dinerMenu = dinerMenu; 8 this.pancakeMenu = pancakeMenu; 9 } 10 public void PrintMenu() 11 { 12 Iterator dinerIterator = dinerMenu.CreateIterator(); 13 Iterator pancakeIterator = pancakeMenu.CreateIterator(); 14 Console.WriteLine("Diner@@@@@@@"); 15 PrineMenu(dinerIterator); 16 Console.WriteLine("Pancake@@@@@@"); 17 PrineMenu(pancakeIterator); 18 } 19 20 private void PrineMenu(Iterator iterator) 21 { 22 while (iterator.HaveNext()) 23 { 24 MenuItem item = (MenuItem)iterator.Next(); 25 Console.WriteLine(string.Format("{0} {1} {2}",item.Name,item.Description,item.Price)); 26 } 27 } 28 }
测试代码
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 DinerMenu dinerMenu = new DinerMenu(); 6 PancakeHouseMenu pancakeMenu = new PancakeHouseMenu(); 7 Waitress waitress = new Waitress(dinerMenu, pancakeMenu); 8 waitress.PrintMenu(); 9 Console.ReadKey(); 10 } 11 }
单一职责原则:一个类应该只有一个引起变化的原因。
比如前文中的菜单,使用迭代器之前,菜单既要负责存储项目,又要负责展示,每次给菜单新增内容时都要都是修改这两方面的功能,造成了维护上的不便,而维护上的不便有可能带来逻辑上的混乱进而引发bug。可见,单一职责原则是很有指导意义的。