学习设计模式《十三》——迭代器模式
一、基础概念
迭代器模式的本质是【控制访问聚合对象中的元素】;
迭代器模式的功能:主要在于提供对聚合对象的迭代访问。迭代器就围绕着这个【访问】做文章,延伸出很多功能来,如:
1、以不同的方式遍历聚合对象(如:向前、向后等);
2、对同一个聚合同时进行多个遍历;
3、以不同的遍历策略来遍历集合(如是否需要过滤等);
4、多态迭代(即:为不同的聚合结构提供统一的迭代接口,也就是说通过一个迭代接口可以访问不同的聚合结构)
注意:多态迭代可能会带来类型安全问题,可以考虑使用泛型。
迭代器模式的关键思想:就是把聚合对象的遍历和访问从聚合对象中分离出来,放入单独的迭代器中(这样聚合对象会变得简单一些;而且迭代器和聚合对象可以独立地变化和发展,会加强系统的灵活性)。
内部迭代器和外部迭代器:
1、内部迭代器:指的是由迭代器自己来控制迭代下一个元素的步骤,客户端无法干预(因此,如果想要在迭代的过程中完成工作的话,客户端就需要把操作传递给迭代器,迭代器在迭代的时候会在每个元素上执行这个操作)
2、外部迭代器:指的是由客户端来控制迭代下一个元素的步骤;
总体来说外部迭代器比内部迭代器要灵活一些,因此我们常见的实现也多属于外部迭代器。
带迭代策略的迭代器:
1、带迭代策略的迭代器【最典型的就是实现过滤功能的迭代器】(即:在实际的开发中,对于经常被访问的一些数据 可以使用缓存,把这些数据存放在内存中。但是不同的业务功能需要访问的数据是不同的,还有不同的业务访问权限能访问的数据也是不同的。对于这种情况,就可以使用实现过滤功能的迭代器,让不同功能使用不同的迭代器来访问。也可以结合策略模式来实现)。
2、谁定义遍历算法的问题:在迭代器模式中,常见的可用来定义遍历算法的地方是【聚合对象本身】【迭代器负责遍历算法】:
【聚合对象本身】定义遍历算法的情况下,通常会在遍历过程中,用迭代器来存储当前迭代的状态,这种迭代器被称为游标(因为它被用来指示当前的位置)。
【迭代器中定义遍历算法】情况下,如果实现遍历算法需要访问聚合对象的私有遍历,那么将遍历算法放入迭代器中会破坏聚合对象的封装性。
现实开发中,具体使用哪一种方式,需要根据具体的问题具体分析。
双向迭代器:可以同时向前向后遍历数据的迭代器。
序号 | 迭代器模式的优点 |
1 | 更好的封装性(迭代器模式可以让你访问一个聚合对象的内容,而无须暴露该聚合对象的内部表示,从而提高聚合对象的封装性; 《1》可以以不同的遍历方式来遍历一个聚合; 《2》 使用迭代器模式,使得聚合对象的内容和具体的迭代算法分离开; 《3》有了迭代器的接口,则聚合本身就不需要再定义这些接口了,从而简化了聚合的接口定义) |
2 | 简化客户端调用(迭代器为遍历不同的聚合对象提供了一个统一的接口,使得客户端遍历聚合对象的内容变得更简单; 同一个聚合上可以有多个遍历;每个迭代器保持它自己的遍历状态,因此可以对同一个聚合对象同时进行多个遍历) |
何时选用迭代器模式?
1、希望提供访问一个聚合对象的内容,但是又不想暴露它的内部表示时候,可使用迭代器模式来提供迭代器接口,从而让客户端只是通过迭代器的接口来访问聚合对象,而无须关系聚合对象的内部实现。
2、希望有多种遍历方式可以访问聚合对象;
3、希望为遍历不同的聚合对象提供一个统一的接口。
二、迭代器模式示例
业务需求:在实际生活中,我们会遇到《整合工资表数据》的情况(即:项目的客户方收购了一家小公司,这家小公司有自己的工资系统,现在需要整合到客户方已有的工资系统中;但是客户方有自己的工资系统,且在内部是采用List来记录工资列表;而新收购的这家公司的工资系统,在内部是采用数组来记录工资列表的;幸运的是,两个系统用来描述工资的数据模式差不多;现在需要整合者两个工资系统的工资数据【最简单的方法就是直接把信收购的这家公司的工资系统也修改为内部使用List来记录工资列表,但是经过仔细查看源代码,发现有很多代码跟这个数组有关,还有很多重要的逻辑处理,只好搁置】现在除了需要把两个工资系统整合起来外,老板还希望能够通过决策辅助系统来统一查看工资数据,不想看到两份不同的工资表,那么该如何实现?)
2.1、不使用迭代器模式的示例
1、定义工资描述模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern
{/// <summary>/// 工资描述模型对象/// </summary>internal class PayModel{/// <summary>/// 支付工资的人员/// </summary>public string? UserName { get; set; }/// <summary>/// 支付的工资数额/// </summary>public double Pay { get; set; }public override string ToString(){string str = $"用户【{UserName}】的工资是【{Pay}】";return str;}}//Class_end
}
2、客户方已有的List工资管理类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern
{/// <summary>/// 客户方已有的工资管理对象/// </summary>internal class PayManager{//聚合对象private List<PayModel> payModels = new List<PayModel>();/// <summary>/// 获取工资列表/// </summary>/// <returns></returns>public List<PayModel> GetPayModels(){return payModels;}/// <summary>/// 计算工资(正常来说应该有很多参数,我们只是演示就从简了)/// </summary>public void CaculatePay(){//计算工资,把工资信息填充到工资条中//为了测试,需要一些测试数据PayModel payModel1 = new PayModel();payModel1.UserName = "张三";payModel1.Pay = 9600;payModels.Add(payModel1);PayModel payModel2 = new PayModel();payModel2.UserName = "李四";payModel2.Pay = 10000;payModels.Add(payModel2);PayModel payModel3 = new PayModel();payModel3.UserName = "王五";payModel3.Pay = 12000;payModels.Add(payModel3);}}//Class_end
}
3、客户方收购的那家使用数组管理的工资管理类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern
{/// <summary>/// 被客户方收购公司的工资管理类/// </summary>internal class SalaryManager{//用数组管理private PayModel[] payModels=new PayModel[3];/// <summary>/// 获取工资列表/// </summary>/// <returns></returns>public PayModel[] GetPayModels(){return payModels;}/// <summary>/// 计算工资(正常来说应该有很多参数,我们只是演示就从简了)/// </summary>public void CaculatePay(){//计算工资,把工资信息填充到工资条中//为了测试,需要一些测试数据PayModel payModel1 = new PayModel();payModel1.UserName = "赵六";payModel1.Pay = 9000;payModels[0]=payModel1;PayModel payModel2 = new PayModel();payModel2.UserName = "孙七";payModel2.Pay = 13000;payModels[1] = payModel2;PayModel payModel3 = new PayModel();payModel3.UserName = "李八";payModel3.Pay = 15000;payModels[2] = payModel3;}}//Class_end
}
4、客户端访问这两个工资列表(需要采用不同的访问方式一个是List一个是数组)
namespace IteratorPattern
{internal class Program{static void Main(string[] args){GetPaySalaryTest();Console.ReadLine();}/// <summary>/// 获取到支付薪水的测试/// </summary>private static void GetPaySalaryTest(){Console.WriteLine("---获取到支付薪水的测试---");//访问集团的工资列表PayManager payManager = new PayManager();//先计算在获取payManager.CaculatePay();List<PayModel> payModelList= payManager.GetPayModels();Console.WriteLine("---集团的工资列表信息:");foreach (var payModel in payModelList){Console.WriteLine(payModel);}//访问新收购公司的工资列表SalaryManager salaryManager = new SalaryManager();salaryManager.CaculatePay();PayModel[] payModelsArray = salaryManager.GetPayModels();Console.WriteLine("---新收购公司的工资信息:");foreach (var payModel in payModelsArray){Console.WriteLine(payModel);}}}//Class_end
}
5、运行结果
2.2、使用迭代器模式的示例
仔细分析我们的业务需求后(发现其实就是要求以一个统一的方式来访问内部实现不同的聚合对象;首先需要把这个统一的访问方式定义出来(接口方法);迭代器迭代的是具体的聚合对象,那么不同的聚合对象就应该有不同的迭代器,为了让迭代器以一个统一的方式来操作聚合对象,因此给所有的聚合对象抽象一个公共的父类,让它来操作聚合对象的公共接口。
1、定义接口和方法可让客户端以统一的方式进行访问
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 迭代器接口,定义访问和遍历元素的操作/// </summary>internal interface Itetator{/// <summary>/// 移动到聚合对象的第一个位置/// </summary>void First();/// <summary>/// 移动到聚合对象的下一个位置/// </summary>void Next();/// <summary>/// 判断是否已经移动到聚合对象的最后一个位置/// </summary>/// <returns>true:表示已经移动到聚合对象的最后一个位置</returns>bool IsDone();/// <summary>/// 获取迭代的当前元素/// </summary>/// <returns></returns>object CurrentItem();}//Interface_end
}
2、分别定义List与数组类继承迭代接口实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 用来实现访问数组的迭代接口/// </summary>internal class ArrayIteratorImpl : Itetator{//用来存放被迭代的聚合对象private SalaryManager salaryManager = null;//用来记录当前迭代的位置索引-1表示刚开始时,迭代器指向聚合对象第一个对象之前private int index = -1;/// <summary>/// 构造函数/// </summary>/// <param name="salaryManager">被客户方收购公司的工资管理类</param>public ArrayIteratorImpl(SalaryManager salaryManager){this.salaryManager = salaryManager;}public object CurrentItem(){return this.salaryManager.Get(index);}public void First(){index = 0;}public bool IsDone(){if (index==this.salaryManager.Size()){return true;}return false;}public void Next(){if (index<salaryManager.Size()){index=index+1;}}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 用来实现访问Collection集合的迭代接口,为了外部统一访问/// </summary>internal class CollectionIteratorImpl : Itetator{//用来存放被迭代的聚合对象private PayManager payManager=null;//用来记录当前迭代的位置索引-1表示刚开始时,迭代器指向聚合对象第一个对象之前private int index=-1;/// <summary>/// 构造函数/// </summary>/// <param name="payManager">客户方已有的工资管理对象</param>public CollectionIteratorImpl(PayManager payManager){this.payManager = payManager;}public object CurrentItem(){return this.payManager.Get(index);}public void First(){index = 0;}public bool IsDone(){if (index == this.payManager.Size()){return true;}return false;}public void Next(){if (index<this.payManager.Size()){index = index + 1;}}}//Class_end
}
3、获取访问聚合的接口
我们现在已经定义好了统一访问聚合的接口,也分别实现了接口方法,现在在客户端需要如何才能获取这个访问聚合的接口呢?且需要以统一的方式来获取【一个简单的方案就是定义一个聚合的接口的接口(即:客户端先通过这个接口来获取访问聚合的接口,然后在访问聚合对象)】
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 聚合对象,定义创建相应迭代器对象的接口/// </summary>internal abstract class Aggregate{//工厂方法,场景相应迭代器对象的接口public abstract Itetator CreateIterator();//获取对象public abstract object Get(int index);//获取对象大小public abstract int Size();}//Class_end
}
4、分别让具体的聚合对象继承抽象来分别提供访问它们的聚合接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 客户方已有的工资管理对象/// </summary>internal class PayManager:Aggregate{//聚合对象private List<PayModel> payModels = new List<PayModel>();/// <summary>/// 获取工资列表/// </summary>/// <returns></returns>public List<PayModel> GetPayModels(){return payModels;}/// <summary>/// 计算工资(正常来说应该有很多参数,我们只是演示就从简了)/// </summary>public void CaculatePay(){//计算工资,把工资信息填充到工资条中//为了测试,需要一些测试数据PayModel payModel1 = new PayModel();payModel1.UserName = "张三";payModel1.Pay = 9600;payModels.Add(payModel1);PayModel payModel2 = new PayModel();payModel2.UserName = "李四";payModel2.Pay = 10000;payModels.Add(payModel2);PayModel payModel3 = new PayModel();payModel3.UserName = "王五";payModel3.Pay = 12000;payModels.Add(payModel3);}public override Itetator CreateIterator(){return new CollectionIteratorImpl(this);}public override object Get(int index){object obj = null;if (index<payModels.Count){obj= payModels[index];}return obj;}public override int Size(){return this.payModels.Count;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 被客户方收购公司的工资管理类/// </summary>internal class SalaryManager:Aggregate{//用数组管理private PayModel[] payModels=new PayModel[3];/// <summary>/// 获取工资列表/// </summary>/// <returns></returns>public PayModel[] GetPayModels(){return payModels;}/// <summary>/// 计算工资(正常来说应该有很多参数,我们只是演示就从简了)/// </summary>public void CaculatePay(){//计算工资,把工资信息填充到工资条中//为了测试,需要一些测试数据PayModel payModel1 = new PayModel();payModel1.UserName = "赵六";payModel1.Pay = 9000;payModels[0]=payModel1;PayModel payModel2 = new PayModel();payModel2.UserName = "孙七";payModel2.Pay = 13000;payModels[1] = payModel2;PayModel payModel3 = new PayModel();payModel3.UserName = "李八";payModel3.Pay = 15000;payModels[2] = payModel3;}public override Itetator CreateIterator(){return new ArrayIteratorImpl(this);}public override object Get(int index){object obj = null;if (index<payModels.Length){obj = payModels[index];}return obj;}public override int Size(){return this.payModels.Length;}}//Class_end
}
5、实现统一的客户端访问
namespace IteratorPattern
{internal class Program{static void Main(string[] args){GetPaySalaryAggregateTest();Console.ReadLine();}/// <summary>/// 获取到支付薪水的聚合测试/// </summary>private static void GetPaySalaryAggregateTest(){Console.WriteLine("---获取到支付薪水的聚合测试---");//访问集团的工资列表AggregateOne.PayManager payManager = new AggregateOne.PayManager();//先计算再获取payManager.CaculatePay();Console.WriteLine("---集团的工资列表信息:");Print(payManager.CreateIterator());//访问新收购公司的工资列表AggregateOne.SalaryManager salaryManager = new AggregateOne.SalaryManager();salaryManager.CaculatePay();Console.WriteLine("---新收购公司的工资信息:");Print(salaryManager.CreateIterator());}private static void Print(AggregateOne.Itetator it){//循环输出聚合对象中的值//首先设置迭代器到第一个元素it.First();while (!it.IsDone()){//取出当前的元素object obj = it.CurrentItem();Console.WriteLine($"当前对象是【{obj}】");//如果没有迭代到最后则继续向下迭代it.Next();}}}//Class_end
}
迭代器的过滤示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.AggregateOne
{/// <summary>/// 用来实现访问Collection集合的迭代接口,为了外部统一访问/// </summary>internal class CollectionIteratorImplFilter : Itetator{//用来存放被迭代的列表private List<PayModel> payModels=null;//用来记录当前迭代的位置索引private int index=0;/// <summary>/// 构造函数/// </summary>/// <param name="payManager">客户方已有的工资管理对象</param>public CollectionIteratorImplFilter(PayManager payManager){/*迭代器过滤示意*///可以先在这里对聚合对象的数据进行过滤(比如:工资必须在10000以下)List<PayModel> tmpPayModels = new List<PayModel>();foreach (var item in payManager.GetPayModels()){if (item.Pay < 10000){tmpPayModels.Add(item);}}//然后把符合要求的数据存放到迭代的列表this.payModels= tmpPayModels;foreach (var item in tmpPayModels){payModels.Add(item);}}public object CurrentItem(){return this.payModels[index];}public void First(){index = 0;}public bool IsDone(){if (index == this.payModels.Count){return true;}return false;}public void Next(){if (index<this.payModels.Count){index = index + 1;}}}//Class_end
}
2.3、翻页迭代——内存顺序翻页
在实际的软件开发中,我们经常用到翻页功能;但翻页功能也有几种实现方法:
《1》纯数据库实现:依靠SQL提供的功能实现翻页,用户每次请求翻页,就会从数据库中获取相应的数据【这是典型的时间换空间策略】。
《2》纯内存实现:就是一次性从数据库中把需要的所有数据都取出来放到内存中,然后用户请求翻页时,从内存中获取相应的数据【这是典型的空间换时间策略】。
在实际的开发中,小型系统一般采用第一种方案,因为内存实在太宝贵了;中大型的系统一般是把两个方案结合起来,综合利用它们的优点,而又规避它们的缺点,从而实现更好的翻页功能。
《3》纯数据库+纯内存实现:思路是如果每页显示10条记录,根据判断,用户很少翻到10页以后,那好,第一次访问的时候,就一次性从数据库中获取前10页的数据(100条记录)都存放在内存里面;这样一来,只要当用户在前10页内进行翻页操作时,就不用再访问数据库了;而是直接从内存获取,速度就快了。当用户想获取第11页的数据,此时就再次访问数据库(那么此时获取多少页数据合适呢?最简单的方法就是继续获取10页的数据;而一种较好的方式是根据访问统计进行衰减访问如折半获取【即第一次获取10页,第二次就获取5页,第三次获取3页,第四次获取2页,第五次获取1页】)。
1、创建翻页迭代器接口
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.PageTurning
{/// <summary>/// 定义翻页访问聚合元素的迭代接口/// </summary>internal interface IAggregateIterator{/// <summary>/// 判断是否还有下一个元素(无论是否足够一页的数据)因为最后哪怕只有一条数据也算一页/// </summary>/// <returns>若有下一个元素则返回true;否则就返回false</returns>bool hasNext();/// <summary>/// 取出后面的几个元素/// </summary>/// <param name="totalNum">需取出的记录条数</param>/// <returns></returns>List<object> Next(int totalNum);/// <summary>/// 判断是否有上一个元素(无论是否够一页的数据)因为最后哪怕只有一条数据也要算一页/// </summary>/// <returns>若有上一个元素则返回true;否则返回false</returns>bool hasPrevious();/// <summary>/// 取出前面的几个元素/// </summary>/// <param name="totalNum">需获取的记录条数</param>/// <returns></returns>List<object> Previous(int totalNum);}//Interface_end
}
2、工资模型保持不变
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.PageTurning
{/// <summary>/// 定义翻页访问聚合元素的迭代接口/// </summary>internal interface IAggregateIterator{/// <summary>/// 判断是否还有下一个元素(无论是否足够一页的数据)因为最后哪怕只有一条数据也算一页/// </summary>/// <returns>若有下一个元素则返回true;否则就返回false</returns>bool hasNext();/// <summary>/// 取出后面的几个元素/// </summary>/// <param name="totalNum">需取出的记录条数</param>/// <returns></returns>List<object> Next(int totalNum);/// <summary>/// 判断是否有上一个元素(无论是否够一页的数据)因为最后哪怕只有一条数据也要算一页/// </summary>/// <returns>若有上一个元素则返回true;否则返回false</returns>bool hasPrevious();/// <summary>/// 取出前面的几个元素/// </summary>/// <param name="totalNum">需获取的记录条数</param>/// <returns></returns>List<object> Previous(int totalNum);}//Interface_end
}
3、薪资管理类实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.PageTurning
{/// <summary>/// 被客户方收购公司的工资管理类/// </summary>internal class SalaryManager{//用数组管理private PayModel[] payModels=new PayModel[5];/// <summary>/// 获取工资列表/// </summary>/// <returns></returns>public PayModel[] GetPayModels(){return payModels;}/// <summary>/// 计算工资(正常来说应该有很多参数,我们只是演示就从简了)/// </summary>public void CaculatePay(){//计算工资,把工资信息填充到工资条中//为了测试,需要一些测试数据PayModel payModel1 = new PayModel();payModel1.UserName = "赵六";payModel1.Pay = 9000;payModels[0]=payModel1;PayModel payModel2 = new PayModel();payModel2.UserName = "孙七";payModel2.Pay = 13000;payModels[1] = payModel2;PayModel payModel3 = new PayModel();payModel3.UserName = "李八";payModel3.Pay = 15000;payModels[2] = payModel3;PayModel payModel4 = new PayModel();payModel4.UserName = "孙七二";payModel4.Pay = 9000;payModels[3] = payModel4;PayModel payModel5 = new PayModel();payModel5.UserName = "李七二";payModel5.Pay = 9000;payModels[4] = payModel5;}public IAggregateIterator CreateIterator(){return new ArrayIteratorImpl(this); }}//Class_end
}
4、创建继承迭代器接口类实现具体的方法
using IteratorPattern.AggregateOne;
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.PageTurning
{/// <summary>/// 用来实现访问数组的迭代接口/// </summary>internal class ArrayIteratorImpl : IAggregateIterator{//用来存放被迭代的数组private PayModel[] payModels = null;//用来记录当前迭代的位置索引private int index = 0;/// <summary>/// 构造函数/// </summary>/// <param name="salaryManager">被客户方收购公司的工资管理类</param>public ArrayIteratorImpl(SalaryManager salaryManager){this.payModels = salaryManager.GetPayModels();}public bool hasNext(){if (payModels!=null && index<=(payModels.Length-1)){return true;}return false;}public bool hasPrevious(){if (payModels!=null && index>0){return true;}return false;}public List<object> Next(int totalNum){List<object> list = new List<object>();int count = 0;while (hasNext() && count<totalNum){list.Add(payModels[index]);//每取走一个值索引就添加1index++;count++;}return list;}public List<object> Previous(int totalNum){List<object> list = new List<object>();int count = 0;//简单的实现就是把索引退回去totalNum个,然后在取值;但事实上这种实现是有可能多退回数据的,//比如:已经到了最后一页,而且最后一页的数据不够一页的数据,那么退回totalNum个索引就退多了//为了示例的简洁性,我们就不去处理了index = index - totalNum;while (hasPrevious() && count<totalNum){list.Add(payModels[index]);index++;count++;}return list;}}//Class_end
}
5、客户端测试翻页
namespace IteratorPattern
{internal class Program{static void Main(string[] args){PageTurningTest();Console.ReadLine();}/// <summary>/// 测试翻页/// </summary>private static void PageTurningTest(){Console.WriteLine("---测试翻页---");//访问新收购公司的工资列表PageTurning.SalaryManager salaryManager = new PageTurning.SalaryManager();salaryManager.CaculatePay();//得到翻页迭代器PageTurning.IAggregateIterator ir=salaryManager.CreateIterator();//获取第一页,每页显示2条List<object> list = ir.Next(2);Console.WriteLine($"第一页数据:");Print2(list);//获取第二页,每页显示2条List<object> list2 = ir.Next(2);Console.WriteLine($"第二页数据:");Print2(list2);//向前翻一页(即再次获取第二页)List<object> list3 = ir.Previous(2);Console.WriteLine($"再次获取第二页数据:");Print2(list3);}private static void Print2(List<object> list){foreach (var item in list){Console.WriteLine(item);}}}//Class_end
}
6、运行结果
2.4、翻页迭代——随机翻页
1、定义迭代接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.RandomPageTurning
{/// <summary>/// 定义随机翻页访问聚合元素的迭代接口/// </summary>internal interface IAggregationIterator<T> where T : class{/// <summary>/// 判断是否还有下一个元素,无所谓是否够一页的数据,因为最后哪怕只有一条数据也是算作一页/// </summary>/// <returns>如果有下一个元素则返回true,否则返回false</returns>public bool HasNext();/// <summary>/// 判断是否还有上一个元素,无所谓是否够一页的数据,因为最后哪怕只有一条数据也是算作一页/// </summary>/// <returns>如果有上一个元素则返回true,否则返回false</returns>public bool HasPrevious();/// <summary>/// 取指定页的数据/// </summary>/// <param name="pageNum">需获取数据的页码</param>/// <param name="pageShow">每页展示的数据条数</param>/// <returns></returns>public T[] GetPageDatas(int pageNum,int pageShow);}//Interface_end
}
2、定义具体的类继承该接口并实现方法内容
using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.RandomPageTurning
{internal class ArrayIteratorImpl : IAggregationIterator<PayModel>{//用来存放被迭代的数组private PayModel[] payModels = null;//用来记录当前迭代到的索引位置private int index = 0;/// <summary>/// 构造函数/// </summary>/// <param name="salaryManager">薪资管理对象</param>public ArrayIteratorImpl(SalaryManager salaryManager){this.payModels = salaryManager.GetPayModels();}public PayModel[] GetPageDatas(int pageNum, int pageShow){PayModel[] payModelArray = new PayModel[pageShow];//需要先计算需获取的数据开始条数和结束条数int start = (pageNum - 1) * pageShow;int end = start + pageShow - 1;//控制start的边界,最小是0if (start<0){start = 0;}//控制end的边界,最大是数组的最大索引if (end>this.payModels.Length-1){end = this.payModels.Length - 1;}//每次取值都是从头开始循环,所以设置index=0index = 0;while (HasNext() && start<=end){payModelArray[index] = payModels[start];start++;index++;}return payModelArray;}public bool HasNext(){if (payModels!=null && index<payModels.Length-1){return true;}return false;}public bool HasPrevious(){if (payModels!=null && index>0){return true;}return false;}}//Class_end
}
3、给客户收购公司的薪水管理类新增该迭代接口的实例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace IteratorPattern.RandomPageTurning
{/// <summary>/// 被客户方收购公司的工资管理类/// </summary>internal class SalaryManager{//用数组管理private PayModel[] payModels=new PayModel[5];/// <summary>/// 获取工资列表/// </summary>/// <returns></returns>public PayModel[] GetPayModels(){return payModels;}/// <summary>/// 计算工资(正常来说应该有很多参数,我们只是演示就从简了)/// </summary>public void CaculatePay(){//计算工资,把工资信息填充到工资条中//为了测试,需要一些测试数据PayModel payModel1 = new PayModel();payModel1.UserName = "赵六";payModel1.Pay = 9000;payModels[0] = payModel1;PayModel payModel2 = new PayModel();payModel2.UserName = "孙七";payModel2.Pay = 13000;payModels[1] = payModel2;PayModel payModel3 = new PayModel();payModel3.UserName = "李八";payModel3.Pay = 15000;payModels[2] = payModel3;PayModel payModel4 = new PayModel();payModel4.UserName = "孙七二";payModel4.Pay = 9000;payModels[3] = payModel4;PayModel payModel5 = new PayModel();payModel5.UserName = "李七二";payModel5.Pay = 9000;payModels[4] = payModel5;}public IAggregationIterator<PayModel> CreateIterator(){return new ArrayIteratorImpl(this);}}//Class_end
}
4、客户端测试随机翻页
namespace IteratorPattern
{internal class Program{static void Main(string[] args){RandomPageTurningTest();Console.ReadLine();}/// <summary>/// 测试随机翻页/// </summary>private static void RandomPageTurningTest(){Console.WriteLine("---测试随机翻页---");//访问新收购公司的工资列表RandomPageTurning.SalaryManager salaryManager = new RandomPageTurning.SalaryManager();//先计算工资数据salaryManager.CaculatePay();//得到翻页迭代器RandomPageTurning.IAggregationIterator<RandomPageTurning.PayModel> it = (RandomPageTurning.IAggregationIterator<RandomPageTurning.PayModel>)salaryManager.CreateIterator();//获取第一页数据,每页显示2条RandomPageTurning.PayModel[] payModelArray1 = it.GetPageDatas(1,2);Console.WriteLine($"第一页的数据是:");Print3(payModelArray1);//获取第二页数据,每页显示2条RandomPageTurning.PayModel[] payModelArray2 = it.GetPageDatas(2, 2);Console.WriteLine($"第二页的数据是:");Print3(payModelArray2);//获取第一页数据,每页显示2条RandomPageTurning.PayModel[] payModelArray3 = it.GetPageDatas(1, 2);Console.WriteLine($"第一页的数据是:");Print3(payModelArray3);//获取第三页数据,每页显示2条RandomPageTurning.PayModel[] payModelArray4 = it.GetPageDatas(3, 2);Console.WriteLine($"第三页的数据是:");Print3(payModelArray4);}private static void Print3(RandomPageTurning.PayModel[] payModelArray){foreach (var item in payModelArray){Console.WriteLine(item);}}}//Class_end
}
6、测试结果
三、项目源码工程
kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern