C#设计模式实现之迭代器模式
作者:SpringSun 发布时间:2023-05-31 19:21:58
目录
前言:
一、餐馆合并菜单
二、改进菜单实现
三、迭代器模式
总结
前言:
迭代器模式平时用的不多,因为不管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);
}
}
}
迭代器模式定义:
迭代器模式:提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露其内部的表示。
迭代器模式让我们能游走于集合内的每一个元素,而又不暴露其内部的表示。
把游走的任务放在迭代器上,而不是集合上。这样简化了集合的接口和实现,也让责任各得其所。
来源:https://juejin.cn/post/6994227370890641421
猜你喜欢
- 使用AS创建ADIL文件时AS会在main文件夹下给我们生成一个aidl文件夹和一个相同包名的包,通常我们会把所有和ADIL相关的类或文件放
- renameTo方法public boolean renameTo(File dest),File类中的renameTo方法可以操作文件或目
- 不论是float 还是double都是浮点数,而计算机是二进制的,浮点数会失去一定的精确度。Java在java.math包中提供的API类B
- 之前我们在使用vue进行 h5 表单录入的过程中,遇到了Android软键盘弹出,覆盖 h5页面 输入框 问题,在此进行回顾并分享给大家:系
- 问题描述问题原因出现该问题的原因是项目Project当中的jdk与电脑当中的jdk版本不一致造成的。解决方法1、查看本机的jdk版本:命令提
- 1.图的遍历从图中某一顶点出发访问图中其余顶点,且每个顶点仅被访问一次图的遍历有两种深度优先遍历DFS、广度优先遍历BFS2.深度优先遍历深
- 了解过spring-Boot这个技术的,应该知道Spring-Boot的核心配置文件application.properties,当然也可以
- 目录一、抽象类1.抽象类概述1.1 为什么要有抽象类?(抽象类的作用)1.2 抽象类的定义2. 抽象类特点3.抽象类成员特点4.抽象类案例二
- 正常状态是UP,跳闸是⼀种状态CIRCUIT_OPEN,可以通过/health查看,前提是工程中需要引入SpringBoot的actuato
- 提到类型转换,首先要明确C#中的数据类型,主要分为值类型和引用类型:1.常用的值类型有:(struct)整型家族:int,byte,char
- mybatis的原身是ibatis,现在已经脱离了apache基金会,新官网是http://www.mybatis.org/。在研究Myba
- 一、SpringMVC使用1.工程创建创建maven工程。添加java、resources目录。引入Spring-webmvc 依赖。<
- 在上节使用了H2之后感觉很爽,很轻便,正好有个项目要求简单,最好不适用外部数据库,于是就想着把H2数据库集成进来,这个系统已经存在了一个Or
- 目录Spring自动注入失败如何解决?回答注入你的bean使用@Configurable手动查找bean:不推荐Spring自动注入失败如何
- Spring BeanPostProcessor执行顺序首先 Spring 通过调用构造方法创建 User 对象;User 对象创建好之后,
- JavaConfig,是在 Spring 3.0 开始从一个独立的项目并入到 Spring 中的。JavaConfig 可以看成一个用于完成
- 读取文件流时,经常会遇到乱码的现象,造成乱码的原因当然不可能是一个,这里主要介绍因为文件编码格式而导致的乱码的问题。首先,明确一点,文本文件
- 今天遇到了一个比较有意思的问题,从服务器上封装好的java.sql.timestamp对象返回到本地客户端程序后与数据库中的时间相差了整整1
- 数据结构是数据存储的方式,算法是数据计算的方式。所以在开发中,算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词,如果各位铁粉
- springboot扩展MVC自定义 config -> SpringMvcConfig.java下边就是扩展springMVC的模板