zoukankan      html  css  js  c++  java
  • Head First设计模式——迭代器模式

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

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

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

    一、餐馆合并菜单

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

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

      菜单项

        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管理,不限制长度

        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

        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;
            }
        }
    

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

                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

                Iterator iterator = breakFastMenu.CreateIterator();
                while (iterator.HasNext)
                {
                    MenuItme menuItme = iterator.Next();
    
                }
    

      

      对于数组

                Iterator iterator = dinerFastMenu.CreateIterator();
                while (iterator.HasNext)
                {
                    MenuItme menuItme = iterator.Next();
    
                }

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

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

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

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

        public interface Iterator
        {
            bool HasNext();
            Object Next();
        }

      加入一个晚餐菜单迭代器

        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;
            }
        }
    

      用迭代器改写晚餐菜单

        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;
            //}
        }
    

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

        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;
            }
        }
    

      用迭代器改写早餐菜单

        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接口

         public interface Menu
        {
            Iterator CreateIterator();
        }
    

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

        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);
                }
            }
        }
    

      

    迭代器模式定义:

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

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

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

  • 相关阅读:
    luogu_P3195 [HNOI2008]玩具装箱TOY
    CF_837D
    luogu_P3584 [POI2015]LAS
    大一下存活纪实
    Mathematical Logic for Computer Science 读书笔记
    集训补题合集
    图论笔记4 平面图与可平面图
    软件分析笔记3 DFA
    软件分析笔记2 IR
    软件分析笔记1 Intro
  • 原文地址:https://www.cnblogs.com/SunSpring/p/11993801.html
Copyright © 2011-2022 走看看