Head First设计模式——迭代器模式

前言:迭代器模式平时用的不多,因为不管C#还是Java都已经帮我封装了,但是你是否知道平时经常在用的东西本质是怎么回事呢。

看完迭代器模式你就知道C# foreach循环是怎么实现的了,我的另一篇C# Foreach循环本质与枚举器就讲解了foreach的本质,其中用到的就是迭代器模式。

按照惯例,例子走起。(写了几个小时浏览器崩溃,我看见在自动保存啊,结果没内容,再撸一遍精简点的吧)

 

一、餐馆合并菜单

现在有两个餐馆和并,其中一个餐馆做早餐,一个做晚餐。他们都有自己管理菜单的方式,现在两个餐馆合并需要对菜单进行统一管理,先让我来看看他们原来的样子。

两个菜单的菜单项都是一样的

  菜单项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MenuItme
{
    //名字
    public string Name { get; set; }
    //描述
    public string Description { get; set; }
    //是否素菜
    public bool Vegetarian { get; set; }
    //价格
    public double Price { get; set; }
 
    public MenuItme(string name, string description, bool vegetarian, double price) {
        Name = name;
        Description=description;
        Vegetarian = vegetarian;
        Price = price;
    }
}

  早餐菜单,使用List管理,不限制长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BreakFastMenu
{
    private List<MenuItme> menuItmes;
    public BreakFastMenu()
    {
        menuItmes = new List<MenuItme>();
        AddItem("梅菜扣肉饼", "好吃", false, 7);
        //菜单项...
    }
 
    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItme menuItme = new MenuItme(name, description, vegetarian, price);
        menuItmes.Add(menuItme);
    }
 
    public List<MenuItme> GetMenuItmes()
    {
        return menuItmes;
    }
}

  晚餐菜单,使用数组管理,限制长度为6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class DinerMenu
{
    static readonly int Max_Items = 6;
    private int numberOfImtes = 0;
    private MenuItme[] menuItmes;
    public DinerMenu()
    {
        menuItmes = new MenuItme[Max_Items];
        AddItem("爆炒癞蛤蟆", "讲究火候", false, 42);
        //菜单项...
    }
 
    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItme menuItme = new MenuItme(name, description, vegetarian, price);
        if (numberOfImtes >= Max_Items)
        {
            Console.WriteLine("菜单已满");
        }
        else
        {
            menuItmes[numberOfImtes] = menuItme;
            numberOfImtes++;
        }
    }
 
    public MenuItme[] GetMenuItmes()
    {
        return menuItmes;
    }
}

  当两个餐馆合并后需要打印早餐和晚餐菜单给顾客用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BreakFastMenu breakFastMenu = new BreakFastMenu();
List<MenuItme> breakFastMenus = breakFastMenu.GetMenuItmes();
 
DinerMenu dinerMenu = new DinerMenu();
MenuItme[] dinerMenus = dinerMenu.GetMenuItmes();
//打印早餐
for (int i = 0; i < breakFastMenus.Count; i++)
{
    Console.WriteLine(breakFastMenus[i].Name);
}
//打印晚餐
for (int i = 0; i < dinerMenus.Length; i++)
{
    Console.WriteLine(dinerMenus[i].Name);
}

按照这种做法我们总是需要处理两个菜单,如果要打印素食,那么也需要循环遍历两个菜单。

假如加入第三家餐厅合并,我们就需要循环处理三次,显然这种方式会让我们系统难以维护。

接下来看我们如何进行改进

二、改进菜单实现

计模式就是要封装变化的部分,很明显,这里变化是:不同的集合类所造成的遍历,我们如何封装遍历集合

不管早餐还是晚餐我们都要用到中括号[ ] 来取菜单项,集合长度来限制长度。

现在我们要创建一个对象,将他称为迭代器(Iterator),利用它来封装“遍历集合内的每个对象的过程”。

  对于List

1
2
3
4
5
6
Iterator iterator = breakFastMenu.CreateIterator();
while (iterator.HasNext)
{
    MenuItme menuItme = iterator.Next();
 
}

  

  对于数组

1
2
3
4
5
6
Iterator iterator = dinerFastMenu.CreateIterator();
while (iterator.HasNext)
{
    MenuItme menuItme = iterator.Next();
 
}

现在两个集合的遍历都统一了,而这种方式正是迭代器模式。关于迭代器我们需要知道的第一件事情,就是它依赖于一个迭代器接口。

这个接口可能有HasNext()方法高数我们是否在这个集合中还有更多的元素。

Next()方法返回这个集合中的下一个对象。一旦我们有了这个接口,就可以为各种对象集合实现迭代器。

现在我们对晚餐菜单进行改造,首先我们需要定义一个迭代器接口

1
2
3
4
5
public interface Iterator
{
    bool HasNext();
    Object Next();
}

  加入一个晚餐菜单迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class DinerMenuIterator : Iterator
{
    MenuItme[] menuItmes;
    int position = 0;
 
    public DinerMenuIterator(MenuItme[] menuItmes)
    {
        this.menuItmes = menuItmes;
    }
    public bool HasNext()
    {
        //由于数组是固定长度,不仅要检查数组,还要检查指定位置是否为空,如果为空后面就没有菜单项了
        if (position >= menuItmes.Length || menuItmes[position] == null)
            return false;
        else
            return true;
    }
 
    public object Next()
    {
        MenuItme menuItme = menuItmes[position];
        position++;
        return menuItme;
    }
}

  用迭代器改写晚餐菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DinerMenu
{
    static readonly int Max_Items = 6;
    private int numberOfImtes = 0;
    private MenuItme[] menuItmes;
    public DinerMenu()
    {
        menuItmes = new MenuItme[Max_Items];
        AddItem("爆炒癞蛤蟆", "讲究火候", false, 42);
        //菜单项...
    }
 
    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItme menuItme = new MenuItme(name, description, vegetarian, price);
        if (numberOfImtes >= Max_Items)
        {
            Console.WriteLine("菜单已满");
        }
        else
        {
            menuItmes[numberOfImtes] = menuItme;
            numberOfImtes++;
        }
    }
    public Iterator CreateIterator()
    {
        return new DinerMenuIterator(menuItmes);
    }
    //public MenuItme[] GetMenuItmes()
    //{
    //    return menuItmes;
    //}
}

  同理我们为早餐加入迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class BreakFastIterator: Iterator
{
    List<MenuItme> menuItmes;
    int position = 0;
 
    public BreakFastIterator(List<MenuItme> menuItmes)
    {
        this.menuItmes = menuItmes;
    }
    public bool HasNext()
    {
        if (position >= menuItmes.Count)
            return false;
        else
            return true;
    }
 
    public object Next()
    {
        MenuItme menuItme = menuItmes[position];
        position++;
        return menuItme;
    }
}

  用迭代器改写早餐菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class BreakFastMenu
{
    private List<MenuItme> menuItmes;
    public BreakFastMenu()
    {
        menuItmes = new List<MenuItme>();
        AddItem("梅菜扣肉饼", "好吃", false, 7);
        //菜单项...
    }
 
    public void AddItem(string name, string description, bool vegetarian, double price)
    {
        MenuItme menuItme = new MenuItme(name, description, vegetarian, price);
        menuItmes.Add(menuItme);
    }
    public Iterator CreateIterator()
    {
        return new BreakFastIterator(menuItmes);
    }
    //public List<MenuItme> GetMenuItmes()
    //{
    //    return menuItmes;
    //}
}

  好了,让我们试一试迭代器工作情况

 

三、迭代器模式

 经过第二步我们基本已经实现迭代器模式,最后我们再改良一下打印菜单,并对菜单进行统一接口的管理。

定义一个Menu接口

1
2
3
4
public interface Menu
{
    Iterator CreateIterator();
}

让早餐晚餐都实现Menu接口,并封装一个新的菜单打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class NewMenu
{
    Menu breakFastMenu;
    Menu dinerMenu;
    public NewMenu(Menu breakFastMenu, Menu dinerMenu) {
        this.breakFastMenu = breakFastMenu;
        this.dinerMenu = dinerMenu;
    }
 
    public void PrintMenu() {
 
 
        Iterator breakFastIterator = breakFastMenu.CreateIterator();
        Console.WriteLine("新菜单--------早餐");
        PrintMenu(breakFastIterator);
        Console.WriteLine("新菜单--------晚餐");
        Iterator dinerIterator = dinerMenu.CreateIterator();
        PrintMenu(dinerIterator);
    }
 
    private void PrintMenu(Iterator iterator) {
        while (iterator.HasNext())
        {
            //取得下一个项
            MenuItme menuItme = (MenuItme)iterator.Next();
            Console.WriteLine(menuItme.Name);
        }
    }
}

  

迭代器模式定义:

迭代器模式:提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式让我们能游走于集合内的每一个元素,而又不暴露其内部的表示。

把游走的任务放在迭代器上,而不是集合上。这样简化了集合的接口和实现,也让责任各得其所。

posted @   XSpringSun  阅读(658)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示