­

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

  • 2019 年 10 月 8 日
  • 筆記

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