使用C#(.NETCore)实现迭代器设计模式(IteratorPattern)yangxupro

本文的概念来自深入浅出设计模式一书

有两个饭店合并了,它们各自有自己的菜单.饭店合并之后要保留这两份菜单.

这两个菜单是这样的:

菜单项MenuItem的代码是这样的:

最初我们是这样设计的,这是第一份菜单:

这是第2份菜单:

问题就是多个菜单把事情变复杂了.例如:如果一个服务员需要使用两份菜单的话,那么她就无法很快的告诉客户有哪些菜是适合素食主义者的了.

服务员还有可能有这些需求:

打印菜单,打印早餐菜单,打印午餐菜单,打印素食菜单,判断某个菜是否是素食的.

首先我们尝试一下如何实现打印菜单:

1.调用两个菜单上面的getMenuItem()方法来获取各自的菜单项,由于它们的菜单不同,所以需要写两段代码:

2.打印两个菜单的菜单项,同样也是两套代码:

3.如果还有一份菜单,那么就需要写三套代码....

现在就很麻烦了.

如果能找到一种方式让这两个菜单同时实现一个接口就好了.我们已经知道,要把变化的部分封装起来.

什么是变化的部分由于不同对象集合引起的遍历操作.

那我们试试;

1.想要遍历早餐项,我们使用ArrayList的size()和get()方法:

2.想要遍历午餐项,我们需要使用Array的length成员变量以及通过索引访问数组:

3.如果我们创建一个对象,把它叫做迭代器,让它来封装我们遍历集合的方式怎么样

这里,我们需要早餐菜单创建一个迭代器,如果还有剩余的菜单项没有遍历完,就获取下一个菜单项.

4.让我们在Array上试试:

首先你需要知道这种模式依赖于一个迭代器接口.例如这个:

hasNext()方法告诉我们集合中是否还有剩余的条目没有遍历到.

next()方法返回下一个条目.

有了这个接口,我们可以在任何一种集合上实现该接口.:

定义迭代器接口:

然后再DinerMenu上实现迭代器接口:

然后使用迭代器来修改DinerMenu菜单:

注意:不要直接返回集合,因为这样会暴露内部实现.

createIterator()方法返回的是迭代器的接口,客户并不需要知道DinerMenu是如何维护菜单项的,也不需要DinerMenu的迭代器是如何实现的.它只是用迭代器来遍历菜单里面的条目.

最后服务员的代码如下:

测试代码:

我们只是为菜单添加了createIterator()方法.

而现在,菜单的实现被封装了,服务员不知道菜单是如何保存菜单项的.

我们所需要的只是一个循环,它可以多态的处理实现了迭代器接口的集合.

而服务员使用的是迭代器接口.

现在呢,菜单还没有共同的接口,这意味着服务员仍然被绑定在两个具体的菜单类上,一会我们再说这个.

目前就是两个菜单实现了同一套方法,但是还没有实现同一个接口.

菜单项MenuItem:

namespaceIteratorPattern.Menus{publicclassMenuItem{publicstringName{get;}publicstringDescription{get;}publicboolVegetarian{get;}publicdoublePrice{get;}publicMenuItem(stringname,stringdescription,boolvegetarian,doubleprice){Name=name;Description=description;Vegetarian=vegetarian;Price=price;}}}迭代器接口IMyIterator:

namespaceIteratorPattern.Abstractions{publicinterfaceIMyIterator{boolHasNext();objectNext();}}两个菜单迭代器:

usingIteratorPattern.Abstractions;usingIteratorPattern.Menus;namespaceIteratorPattern.MenuIterators{publicclassMyDinerMenuIterator:IMyIterator{privatereadonlyMenuItem[]_menuItems;privateint_position;publicMyDinerMenuIterator(MenuItem[]menuItems){_menuItems=menuItems;}publicboolHasNext(){if(_position>=_menuItems.Length||_menuItems[_position]==null){returnfalse;}returntrue;}publicobjectNext(){varmenuItem=_menuItems[_position];_position++;returnmenuItem;}}}usingSystem.Collections;usingIteratorPattern.Abstractions;namespaceIteratorPattern.MenuIterators{publicclassMyPancakeHouseMenuIterator:IMyIterator{privatereadonlyArrayList_menuItems;privateint_position;publicMyPancakeHouseMenuIterator(ArrayListmenuItems){_menuItems=menuItems;}publicboolHasNext(){if(_position>=_menuItems.Count||_menuItems[_position]==null){returnfalse;}_position++;returntrue;}publicobjectNext(){varmenuItem=_menuItems[_position];_position++;returnmenuItem;}}}两个菜单:

usingSystem;usingIteratorPattern.Abstractions;usingIteratorPattern.Menus;namespaceIteratorPattern.Waitresses{publicclassMyWaitress{privatereadonlyMyPancakeHouseMenu_pancakeHouseMenu;privatereadonlyMyDinerMenu_dinerMenu;publicMyWaitress(MyPancakeHouseMenupancakeHouseMenu,MyDinerMenudinerMenu){_pancakeHouseMenu=pancakeHouseMenu;_dinerMenu=dinerMenu;}publicvoidPrintMenu(){varpancakeIterator=_pancakeHouseMenu.CreateIterator();vardinerIterator=_dinerMenu.CreateIterator();Console.WriteLine("MENU\n--------------\nBREAKFIRST");PrintMenu(pancakeIterator);Console.WriteLine("\nLUNCH");PrintMenu(dinerIterator);}privatevoidPrintMenu(IMyIteratoriterator){while(iterator.HasNext()){varmenuItem=iterator.Next()asMenuItem;Console.Write($"{menuItem.Name},");Console.Write($"{menuItem.Price}--");Console.WriteLine($"{menuItem.Description}");}}}}测试:

Java里面内置了Iterator接口,我们刚才是手写了一个Iterator迭代器接口.Java内置的定义如下:

注意里面这个remove()方法,我们可能不需要它.

remove()方法是可选实现的,如果你不想让集合有此功能的话,就应该抛出NotSupportedException(C#的).

由于PancakeHouseMenu使用的是ArrayList,而ArrayList已经实现了该接口,那么:这样简单改一下就可以:

针对DinerMe菜单,还是需要手动实现的:

最后别忘了给菜单规定一个统一的接口:

服务员Waitress类里面也使用Menu来代替具体的菜单,这样也减少了服务员对具体类的依赖(针对接口编程,而不是具体的实现):

最后看下改进后的设计类图:

迭代器模式提供了一种访问聚合对象(例如集合)元素的方式,而且又不暴露该对象的内部表示.

迭代器模式负责遍历该对象的元素,该项工作由迭代器负责而不是由聚合对象(集合)负责.

类图:

一个类应该只有一个变化发生的原因.

写代码的时候这个原则很容易被忽略掉,只能通过多检查设计来避免违反原则.

所谓的高内聚,就是只这个类是围绕一套关连的函数而设计的.

遵循该原则的类通常是高内聚的,并且可维护性要比那些多重职责或低内聚的类好.

还需要添加另一份菜单:

这个菜单使用的是HashTable.

首先修改该菜单,让它实现Menu接口:

注意看HashTable的不同之处:

首先通过values()方法获取HashTable的集合对象,这个对象正好实现了Iterator接口,直接调用iterator()方法即可.

最后修改服务员类:

测试:

我们给了服务员一种简单的方式来遍历菜单项,不同的菜单实现了同一个迭代器接口,服务员不需要知道菜单项的实现方法.

我们把服务员和菜单的实现解耦了

而且使服务员可以扩展:

现在有三个菜单,每次再添加一个菜单的时候,你都得相应的添加一套代码,这违反了"对修改关闭,对扩展开放原则".

那我们把这些菜单放到可迭代的集合即可:

菜单接口:

usingSystem.Collections;namespaceIteratorPattern.Abstractions{publicinterfaceIMenu{IEnumeratorCreateIEnumerator();}}

三个菜单:

usingSystem;usingSystem.Collections;usingIteratorPattern.Menus;namespaceIteratorPattern.MenuIterators{publicclassDinerMenuIterator:IEnumerator{privatereadonlyMenuItem[]_menuItems;privateint_position=-1;publicDinerMenuIterator(MenuItem[]menuItems){_menuItems=menuItems;}publicboolMoveNext(){_position++;if(_position>=_menuItems.Length||_menuItems[_position]==null){returnfalse;}returntrue;}publicvoidReset(){_position=-1;}publicobjectCurrent=>_menuItems[_position];}}usingSystem.Collections;usingSystem.Collections.Generic;namespaceIteratorPattern.MenuIterators{publicclassPancakeHouseMenuIterator:IEnumerator{privatereadonlyArrayList_menuItems;privateint_position=-1;publicPancakeHouseMenuIterator(ArrayListmenuItems){_menuItems=menuItems;}publicboolMoveNext(){_position++;if(_position>=_menuItems.Count||_menuItems[_position]==null){returnfalse;}returntrue;}publicvoidReset(){_position=-1;}publicobjectCurrent=>_menuItems[_position];}}服务员:

usingSystem;usingSystem.Collections;usingIteratorPattern.Abstractions;usingIteratorPattern.Menus;namespaceIteratorPattern.Waitresses{publicclassWaitress{privatereadonlyArrayList_menus;publicWaitress(ArrayListmenus){_menus=menus;}publicvoidPrintMenu(){varmenuIterator=_menus.GetEnumerator();while(menuIterator.MoveNext()){varmenu=menuIterator.CurrentasIMenu;PrintMenu(menu.CreateIEnumerator());}}privatevoidPrintMenu(IEnumeratoriterator){while(iterator.MoveNext()){if(iterator.Current!=null){MenuItemmenuItem;if(iterator.CurrentisMenuItemitem){menuItem=item;}else{menuItem=((DictionaryEntry)iterator.Current).ValueasMenuItem;}Console.Write($"{menuItem.Name},");Console.Write($"{menuItem.Price}--");Console.WriteLine($"{menuItem.Description}");}}Console.WriteLine();}}}测试:

staticvoidMenuTestDriveUsingIEnumerator(){varpancakeHouseMenu=newPancakeHouseMenu();vardinerMenu=newDinerMenu();varcafeMenu=newCafeMenu();varwaitress=newWaitress(newArrayList(3){pancakeHouseMenu,dinerMenu,cafeMenu});waitress.PrintMenu();}

THE END
1.简单午餐图片大全高清熊猫办公精心为用户挑选80张高清精美午餐图片、支持专业级午餐设计素材下载,更多风格的午餐,免抠元素,卡通手绘素材图片、图标图案、免抠矢量图,尽在熊猫办公。https://www.tukuppt.com/speciall/wucan3399.html
2.午餐菜单设计午餐菜单模板午餐菜单图片觅知网为您找到96个原创午餐菜单设计图片,包括午餐菜单图片,午餐菜单素材,午餐菜单海报,午餐菜单背景,午餐菜单模板源文件下载服务,包含PSD、PNG、JPG、AI、CDR等格式素材,更多关于午餐菜单素材、图片、海报、背景、插画、配图、矢量、UI、PS、免抠,模板、艺术字、PPhttps://www.51miz.com/so-sucai/3847805.html
3.100道学生午餐的做法步骤图kizzyJ【100道学生午餐】1.拌饭(水煮青菜、炒肉丝、炒西葫芦、炒香菇、木耳、油浸金枪鱼罐头、盐水虾、拌饭海苔、雪碧稀释后的拌饭酱);2.照烧鸡腿小牛饭;3.鲫鱼汤,烧茄子,炒肥肠,辣白菜;4.芹菜炒粉、炸蘑菇、白灼海虾、?大虾、红焖羊小腿;5.红焖羊小腿、凉拌芹菜https://www.xiachufang.com/recipe/106982721/
4.学生这份午餐食谱(附招牌菜做法),高蛋白高膳食纤维,家长收好学生这份午餐食谱(附招牌菜做法),高蛋白高膳食纤维,家长收好 我是饮食健康管理师小辉(营养师)。炎炎夏日,考生们陆续迎来高考、中考和期末考,在这里,作为营养师,我为大家准备了几个营养丰富、蛋白质丰富、膳食纤维丰富的午餐食谱,喜欢家长和孩子们喜欢。食谱1:白米饭+丝瓜炒虾仁+青椒炒牛肉 营养说明:白https://baijiahao.baidu.com/s?id=1801984074431425075&wfr=spider&for=pc
5.西餐菜单设计素材西餐菜单模板背景图2. 午餐菜单 午餐菜单应该包含各种美味的海鲜和肉类,以及蔬菜和甜点。以下是一些午餐菜单设计素材: - 烤牛肉配蘑菇和洋葱 - 意大利面配橄榄油和番茄酱 - 煎鱼配蒜香烤面包和蔬菜 - 烤鸡肉配烤蔬菜和土豆 - 海鲜汤配蔬菜和面包 - 奶油沙拉配生菜、番茄和洋葱 http://coffee.cn/xican/post/308401.html
6.54个免费午餐食谱,您有一条养生福利待接收本文关键词:免费午餐菜单,免费的午餐,免费午餐意思,54个免费午餐食谱大全,2020免费午餐。这就是关于《54个免费午餐食谱,您有一条养生福利待接收》的所有内容,希望对您能有所帮助!更多的知识请继续关注《犇涌向乾》百科知识网站:http://www.029ztxx.com!http://www.029ztxx.com/tg/16809351411164525.html
7.餐厅设计公司:餐厅菜单的种类!(3)晚餐菜单有些餐厅午餐、晚餐菜单分别制作,有的餐厅午餐、晚餐菜单是一份。午餐、晚餐菜单需品种齐全,各式菜式搭配平衡,并有一些反映其特色的拿手菜。(4)夜宵菜单中餐馆使用较为普遍,主要为习惯于夜生活的人而设计,使用时间通常是子夜前后。https://www.hwlsj.com/NewsShow_3336.html
8.外卖午餐图片外卖午餐设计素材红动中国素材网提供38个外卖午餐图片、外卖午餐素材、外卖午餐背景、外卖午餐模板、外卖午餐海报等PS素材下载,包含PSD、AI、PNG、JPG、CDR等格式源文件素材,更多精品外卖午餐设计素材下载,就来红动中国,最后更新于2024-12-03 15:37:15。https://so.redocn.com/waimai/cde2c2f4cee7b2cd.htm
9.迭代器设计模式迭代器设计模式 在这个例子中,我们将实现两个菜单,一个午餐菜单使用ArrayList实现,另一个早餐菜单使用数组实现。然后,使用迭代器模式遍历两个菜单,并将它们合并到一起,最后输出所有菜单项。 步骤: 创建迭代器接口:定义用于遍历集合的标准接口。 实现午餐菜单和早餐菜单类:分别使用ArrayList和数组实现菜单。https://blog.csdn.net/qq_34358193/article/details/141932498
10.菜单设计图片素材模板素材jpg图片格式mac天空素材可以为您的创意项目找到完美的图片素材,免费批量打包下载。菜单设计图片素材包含:背景,传统,颜色,摘要,插图,卡片,框架,设置,晚餐,美味书签等素材元素_jpg图片格式_4149X4150像素https://www.mac69.com/material/656311.html
11.10个美丽的餐厅食物菜单设计,激发你的灵感我们尚略餐饮设计公司希望以上这些将对您进行一份令人印象深刻的菜单的设计时会有所帮助! 1.行政餐厅菜单设计Mahonia Bahrain 2.部队食物餐饮菜单设计手册 3.黄铜色洋葱主元素餐饮菜单设计 4.小午餐餐饮菜单设计 5. Lavenue餐厅菜单设计 6. Cote餐饮菜单设计 https://www.shinerayad.com/news_info.asp?id=4128
12.设计精美餐厅菜单的10多个技巧51CTO博客反映您餐厅的个性- 您的菜单设计应散发出餐厅的品质和标准。食客可以选择的食物太多了,您应该使用菜单设计来大声喊叫您的食物有什么独特之处,并确保令食客的垂涎。 您应该创建几个菜单 -早餐,午餐和晚餐是否需要其他菜单?如果您在特定时间提供某些餐点,则可能需要为每个服务提供单独的菜单。除了食物菜单,您是否想到过https://blog.51cto.com/u_13102515/6410046
13.作业设计《中国美食》拓展作业任务二:设计午餐菜单 根据提供的食材,制定一份午餐菜单,荤素搭配,营养合理,渗透营养均衡的健康知识。 在家里,家长也可以让孩子参与三餐的菜单设计,积累营养知识。 任务三:做美食,写美食 利用周末,动手做一道美食,写一则美食日记。 写清楚这道美食是什么,怎么制作的,制作过程中发生了什么有趣的小故事。美食做好后,https://www.360doc.cn/article/69489493_1118483192.html
14.主题宴会设计(通用11篇)2、上海ApEC会议菜单 2001年10月,亚太经济合作组织(ApEC)第九次领导人非正式会议在中国上海举行。在这次会议中,有一场最高规格的工作午餐,其规格比国宴还高,午餐菜单是一张有代表性的主题菜单。设计者独具匠心,所用的原料是很平常的鸡、鸭、鳕鱼、蟹、虾仁等,经厨师精心烹饪,成了一道道让客人赞不绝口的蕴涵中https://www.360wenmi.com/f/file3777eh4b.html