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


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

  • 相关阅读:
    什么是模板方法模式?
    在多线程环境下,SimpleDateFormat 是线程安全的吗?
    抽象类是什么?它与接口有什么区别?你为什么要使用过 抽象类?
    依赖注入和工程模式之间有什么不同?
    什么时候使用访问者模式?
    Java 中,受检查异常 和 不受检查异常的区别?
    说出 5 个 JDK 1.8 引入的新特性?
    什么是领域驱动设计?
    列举 IoC 的一些好处?
    什么是 Spring 配置文件?
  • 原文地址:https://www.cnblogs.com/az4215/p/11630073.html
Copyright © 2020-2023  润新知