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

转载 酷学大叔 2019/10/8 21:28:06

迭代器模式()迭代器模式允许你访问一个数据项序列中的所有元素,而无须关心序列是什么类型(数组、链表、列表或任何其他类型)。它能有效地构建一个数据管道,经过一系列不同的转换或过滤后再从管道的另一端出来。迭代器模式就是提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知

迭代器模式(Iterator):

  迭代器模式允许你访问一个数据项序列中的所有元素,而无须关心序列是什么类型(数组、链表、列表或任何其他类型)。它能有效地构建一个数据管道,经过一系列不同的转换或过滤后再从管道的另一端出来。迭代器模式就是提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示。

迭代器模式的角色:

    

  1)抽象迭代器(Iterator):接口声明了遍历集合所需的操作(获取下一个元素、获取当前位置和重新开始迭代等)。

  2)具体迭代器(ConcreteIterator):实现遍历集合的一种特定算法。迭代器对象必须跟踪自身遍历的进度。这使得多个迭代器可以相互独立地遍历同一个集合。

  3)抽象聚合(Aggregate):接口声明一个或多个方法来获取与集合兼容的迭代器。返回方法的类型必须被声明为迭代器接口。

  4)具体聚合(ConcreteAggregate):会在客户端请求迭代器时返回一个特定的具体迭代器类实体

  5)客户端(Client):通过集合和迭代器的接口与两者进行交互 这样一来客户端无需与具体类进行耦合 允许同一客户端代码使用各种不同的集合和迭代器

示例:

  先假设有两家餐厅,主营业务不同,一家是早餐店,一家是晚餐店。 

  1 /// <summary>
/// 菜单明细项
/// </summary>
public class MenuItem
{
    private string name;
    private string description;
    private bool vegetarin;
    private double price;

    public MenuItem(string name, string description, bool vegetarin, double price)
    {
        this.name = name;
        this.description = description;
        this.vegetarin = vegetarin;
        this.price = price;
    }

    public string GetName()
    {
        return this.name;
    }

    public double GetPrice()
    {
        return price;
    }

    public bool IsVegetarian()
    {
        return vegetarin;
    }

    public string GetDescription()
    {
        return description;
    }
}

/// <summary>
/// 早餐菜单
/// </summary>
public class BreakfastMenu
{
    private List<MenuItem> menuItems;

    public BreakfastMenu()
    {
        menuItems = new List<MenuItem>();
        AddItem("牛奶", "牛奶description", false, 3.0);
        AddItem("油条", "油条description", false, 1.0);
        AddItem("馒头", "馒头description", true, 1.0);
        AddItem("豆浆", "DoujiangDescription", true, 1.5);
    }

    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.Add(menuItem);
    }

    public List<MenuItem> GetMenuItems()
    {
        return menuItems;
    }
}

/// <summary>
/// 晚餐菜单
/// </summary>
public class DinnerMenu
{
    private static readonly int Max_ITEMS = 6;
    private int numberOfItems = 0;
    private MenuItem[] menuItems;

    public DinnerMenu()
    {
        menuItems = new MenuItem[Max_ITEMS];
        AddItem("香菇豆腐饭", "香菇豆腐", false, 10.5);
        AddItem("蛋炒饭", "哈哈", false, 8.5);
        AddItem("鱼香肉丝", "你猜", true, 15.5);
    }

    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems > Max_ITEMS)
        {
            Console.WriteLine("菜单已满");
        }
        else
        {
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

    public MenuItem[] GetMenuItems()
    {
        return menuItems;
    }
}

  现在两家合并了,服务员那菜单的时候就要拿两份菜单。

 1 public static void Main(string[] args)
{
    BreakfastMenu breakfastMenu = new BreakfastMenu();
    List<MenuItem> breakfastItems = breakfastMenu.GetMenuItems();

    DinnerMenu dinerMenu = new DinnerMenu();
    MenuItem[] lunchItems = dinerMenu.GetMenuItems();

    for (int i = 0; i < breakfastItems.Count; i++)
    {
        MenuItem menuItem = breakfastItems[i] as MenuItem;
        Console.WriteLine(menuItem.GetName() + " " + menuItem.GetPrice().ToString() + " " + menuItem.GetDescription().ToString());
    }

    for (int j = 0; j < lunchItems.Length; j++)
    {
        MenuItem lunchItem = lunchItems[j];
        if (lunchItem != null)
        {
            Console.WriteLine(lunchItem.GetName() + " " + lunchItem.GetPrice().ToString() + " " + lunchItem.GetDescription().ToString());
        }
    }
}

  我们发现,由于两份菜单数据结构的不同,我们不得不重写多余的代码,显得很臃肿。我们会想:能不能有一个东西能够让我们不需要知道菜单的数据结构,直接就可以获取其内部元素呢?答案是肯定的,这就是我们本节所说的迭代器模式。

  先定义一个接口迭代器并实现晚餐菜单迭代器和晚餐菜单迭代器。

 1 /// <summary>
/// 接口迭代器
/// </summary>
public interface Iterator
{
    /// <summary>
    /// 用来判断下一个元素是否为空
    /// </summary>
    /// <returns></returns>
    bool HasNext();

    /// <summary>
    /// 用来获取当前元素
    /// </summary>
    /// <returns></returns>
    object Next();
}

/// <summary>
/// 早餐菜单迭代器
/// </summary>
public class BreakfastIterator : Iterator
{
    private List<MenuItem> items;
    private int position = 0;

    public BreakfastIterator(List<MenuItem> items)
    {
        this.items = items;
    }

    public bool HasNext()
    {
        return position <= items.Count - 1 && items[position] != null;
    }

    public object Next()
    {
        MenuItem item = items[position];
        position++;
        return item;
    }
}

/// <summary>
/// 晚餐菜单迭代器
/// </summary>
public class DinnerIterator : Iterator
{
    private MenuItem[] items;
    private int position = 0;

    public DinnerIterator(MenuItem[] items)
    {
        this.items = items;
    }

    public bool HasNext()
    {
        return position <= items.Length && items[position] != null;
    }

    public object Next()
    {
        MenuItem item = items[position];
        position++;
        return item;
    }
}

  修改菜单。

 1 /// <summary>
/// 抽象聚合对象,用于创建一个迭代器对象
/// </summary>
public interface IMenu
{
    Iterator CreateIterator();
}

/// <summary>
/// 早餐菜单
/// </summary>
public class BreakfastMenu : IMenu

{
    private List<MenuItem> menuItems;

    public BreakfastMenu()
    {
        menuItems = new List<MenuItem>();
        AddItem("牛奶", "牛奶description", false, 3.0);
        AddItem("油条", "油条description", false, 1.0);
        AddItem("馒头", "馒头description", true, 1.0);
        AddItem("豆浆", "DoujiangDescription", true, 1.5);
    }

    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        menuItems.Add(menuItem);
    }

    //public List<MenuItem> GetMenuItems()
    //{
    //    return menuItems;
    //}

    public Iterator CreateIterator()
    {
        return new BreakfastIterator(menuItems);
    }
}

/// <summary>
/// 晚餐菜单
/// </summary>
public class DinnerMenu : IMenu

{
    private static readonly int Max_ITEMS = 6;
    private int numberOfItems = 0;
    private MenuItem[] menuItems;

    public DinnerMenu()
    {
        menuItems = new MenuItem[Max_ITEMS];
        AddItem("香菇豆腐饭", "香菇豆腐", false, 10.5);
        AddItem("蛋炒饭", "哈哈", false, 8.5);
        AddItem("鱼香肉丝", "你猜", true, 15.5);
    }

    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems > Max_ITEMS)
        {
            Console.WriteLine("菜单已满");
        }
        else
        {
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

    //public MenuItem[] GetMenuItems()
    //{
    //    return menuItems;
    //}

    public Iterator CreateIterator()
    {
        return new DinnerIterator(menuItems);
    }
}

  这个时候,两份餐单的输出是这样的。

 1 public static void Main(string[] args)
{
    IMenu breakfastMenu = new BreakfastMenu();
    IMenu dinnerMenu = new DinnerMenu();
    breakfastMenu.CreateIterator();
    Iterator dinnerIterator = dinnerMenu.CreateIterator();
    Iterator breakfastIterator = breakfastMenu.CreateIterator();

    Print(breakfastIterator);
    Print(dinnerIterator);

    static void Print(Iterator iterator)
    {
        while (iterator.HasNext())
        {
            MenuItem menuItem = (MenuItem)iterator.Next();
            Console.WriteLine(menuItem.GetName() + " " + menuItem.GetPrice().ToString() + " " + menuItem.GetDescription().ToString());
        }
    }
}

迭代器模式适用性:

  1)当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑),可以使用迭代器。

  2)可以减少程序中重复的遍历代码。

  3)如果你希望代码能够遍历不同的甚至是无法预知的数据结构,可以使用迭代器。

迭代器模式的优缺点:

  优点:

    1)它支持以不同的方式遍历一个聚合对象。

    2)迭代器简化了聚合类。

    3)在同一个聚合上可以有多个遍历。

    4)在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。符合OCP原则

  缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

参考:https://www.cnblogs.com/lzhp/p/3427704.html

上一篇:Dropout原理与实现

下一篇:【算法随记五】使用FFT变换自动去除图像中严重的网纹。

赞(0)

共有 条评论 网友评论

验证码: 看不清楚?
    扫一扫关注最新编程教程