设计模式-行为型-迭代器模式

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

posted @ 2019-10-08 09:38  酷学大叔  阅读(452)  评论(0编辑  收藏  举报