迭代器模式(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 }
View Code
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。可见,单一职责原则是很有指导意义的。