首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

研磨设计方式 之 迭代器模式(Iterator)3——跟着cc学设计系列

2012-11-11 
研磨设计模式 之 迭代器模式(Iterator)3——跟着cc学设计系列14.3? 模式讲解14.3.1? 认识迭代器模式(1)迭代

研磨设计模式 之 迭代器模式(Iterator)3——跟着cc学设计系列

14.3? 模式讲解14.3.1? 认识迭代器模式

(1)迭代器模式的功能

迭代器模式的功能主要在于提供对聚合对象的迭代访问。迭代器就围绕着这个“访问”做文章,延伸出很多的功能来。比如:

    以不同的方式遍历聚合对象,比如向前、向后等对同一个聚合同时进行多个遍历以不同的遍历策略来遍历聚合,比如是否需要过滤等多态迭代,含义是:为不同的聚合结构,提供统一的迭代接口,也就是说通过一个迭代接口可以访问不同的聚合结构,这就叫做多态迭代。上面的示例就已经实现了多态迭代,事实上,标准的迭代模式实现基本上都是支持多态迭代的。

    但是请注意:多态迭代可能会带来类型安全的问题,可以考虑使用泛型。

    (2)迭代器模式的关键思想

    聚合对象的类型很多,如果对聚合对象的迭代访问跟聚合对象本身融合在一起的话,会严重影响到聚合对象的可扩展性和可维护性。

    因此迭代器模式的关键思想就是把对聚合对象的遍历和访问从聚合对象中分离出来,放入到单独的迭代器中,这样聚合对象会变得简单一些;而且迭代器和聚合对象可以独立的变化和发展,会大大加强系统的灵活性。

    (3)内部迭代器和外部迭代器

    所谓内部迭代器,指的是由迭代器自己来控制迭代下一个元素的步骤,客户端无法干预,因此,如果想要在迭代的过程中完成工作的话,客户端就需要把操作传给迭代器,迭代器在迭代的时候会在每个元素上执行这个操作,类似于Java的回调机制。

    所谓外部迭代器,指的是由客户端来控制迭代下一个元素的步骤,像前面的示例一样,客户端必须显示的调用next来迭代下一个元素。

    总体来说外部迭代器比内部迭代器要灵活一些,因此我们常见的实现多属于外部迭代器,前面的例子也是实现的外部迭代器。

    (4)Java中最简单的统一访问聚合的方式

    ?????? 如果只是想要使用一种统一的访问方式来访问聚合对象,在Java中有更简单的方式,简单到几乎什么都不用做,利用Java 5以上版本本身的特性即可。

    ?????? 但是请注意,这只是从访问形式上一致了,但是也暴露了聚合的内部实现,因此并不能算是标准迭代器模式的实现,但是从某种意义上说,可以算是隐含的实现了部分迭代器模式的功能。

    ?????? 那么怎么做呢?

    为了简单,让我们回到没有添加任何迭代器模式的情况下。很简单,只要让聚合对象中的结合实现范型即可,示例如下:

    public class PayManager{

    ??? private List<PayModel> list = new ArrayList<PayModel>();

    ??? /**

    ??? ?* 获取工资列表

    ??? ?* @return 工资列表

    ??? ?*/

    ??? public List<PayModel> getPayList(){

    ?????? return list;

    ??? }

    ?

    ?

    }

    这样一来,客户端的代码就可以改成使用增强的for循环来实现了,对于数组、范型的集合都可以采用一样的方法来实现了,从代码层面上看,就算是统一了访问聚合的方式了,修改后的客户端代码如下:

    public class Client {

    ??? public static void main(String[] args) {

    ?????? //访问集团的工资列表

    ?????? PayManager payManager= new PayManager();

    ?????? //先计算再获取

    ?????? payManager.calcPay();

    ?

    ?????? Collection<PayModel> payList = payManager.getPayList();

    ?????? System.out.println("集团工资列表:");

    ?

    //???? Iterator it = payList.iterator();

    //???? while(it.hasNext()){

    //???????? PayModel pm = (PayModel)it.next();

    //???????? System.out.println(pm);

    //???? }

    ?

    import java.util.Iterator;

    public abstract class Aggregate {

    ??? public abstract Iterator createIterator();

    }

    (3)原来的ArrayIteratorImpl类,实现的接口改变了,实现代码也需要跟着改变,示例代码如下:

    /**

    ?* 用来实现访问数组的迭代接口

    ?*/

    public class ArrayIteratorImpl implements Iterator{

    ??? /**

    ??? ?* 用来存放被迭代的聚合对象

    ??? ?*/

    ??? private SalaryManager aggregate = null;

    ??? /**

    ??? ?* 用来记录当前迭代到的位置索引

    ??? ?*/

    ??? private int index = 0;

    ??? public ArrayIteratorImpl(SalaryManager aggregate){

    ?????? this.aggregate = aggregate;

    ??? }

    ?

    ??? public boolean hasNext() {

    ?????? //判断是否还有下一个元素

    ?????? if(aggregate!=null && index<aggregate.size()){

    ?????????? return true;

    ?????? }

    ?????? return false;

    ??? }

    ??? public Object next() {

    ?????? Object retObj = null;

    ?????? if(hasNext()){

    ?????????? retObj = aggregate.get(index);

    ?????????? //每取走一个值,就把已访问索引加1

    ?????????? index++;

    ?????? }

    ?????? return retObj;

    ??? }

    ??? public void remove() {

    ?????? //暂时可以不实现???

    ??? }

    }

    (4)对于PayManager类,在实现创建迭代器的方法上发生了改变,不再使用自己实现的迭代器,改用java的集合框架实现的迭代器了。示例代码如下:

    public class PayManager extends Aggregate{

    ??? private List<PayModel> list = new ArrayList<PayModel>();

    ??? public List<PayModel> getPayList(){

    ?????? return list;

    ??? }

    ??? public void calcPay(){

    ?????? //计算工资,并把工资信息填充到工资列表里面

    ?????? //为了测试,做点数据进去

    ?????? PayModel pm1 = new PayModel();

    ?????? pm1.setPay(3800);

    ?????? pm1.setUserName("张三");

    ?

    ?????? PayModel pm2 = new PayModel();

    ?????? pm2.setPay(5800);

    ?????? pm2.setUserName("李四");

    ?

    ?????? list.add(pm1);

    ?????? list.add(pm2);

    ??? }??

    ????public?Iterator createIterator() {

    ?????? return list.iterator();

    ??? }

    }

    (5)对于SalaryManager类,除了创建迭代器方法返回的类型改变外,其它都没有改变,还是用ArrayIteratorImpl来实现迭代器。

    (6)接下来写个客户端来测试看看,示例代码如下:

    public class Client {

    ??? public static void main(String[] args) {

    ?????? //访问集团的工资列表

    ?????? PayManager payManager= new PayManager();

    ?????? //先计算再获取

    ?????? payManager.calcPay();

    ?????? System.out.println("集团工资列表:");

    ???????test(payManager.createIterator());

    ?

    ?????? //访问新收购公司的工资列表

    ?????? SalaryManager salaryManager = new SalaryManager();

    ?????? //先计算再获取

    ?????? salaryManager.calcSalary();

    ?????? System.out.println("新收购的公司工资列表:");

    ???????test(salaryManager.createIterator());

    ??? }

    ??? /**

    ??? ?* 测试通过访问聚合对象的迭代器,是否能正常访问聚合对象

    ??? ?* @param it 聚合对象的迭代器

    ??? ?*/

    ??? private static void test(Iterator it){

    ?????? while(it.hasNext()){

    ?????????? PayModel pm = (PayModel)it.next();

    ?????????? System.out.println(pm);

    ?????? }

    ??? }

    }

    很明显改用Java的Iterator来实现,比自己全部重新去做,还是要简单一些的。

    14.3.3? 带迭代策略的迭代器

    由于迭代器模式把聚合对象和访问聚合的机制实现了分离,所以可以在迭代器上实现不同的迭代策略,最为典型的就是实现过滤功能的迭代器。

    ?????? 在实际开发中,对于经常被访问的一些数据可以使用缓存,把这些数据存放在内存中。但是不同的业务功能需要访问的数据是不同的,还有不同的业务访问权限能访问的数据也是不同的,对于这种情况,就可以使用实现过滤功能的迭代器,让不同功能使用不同的迭代器来访问。当然,这种情况也可以结合策略模式来实现。

    ?????? 在实现过滤功能的迭代器中,又有两种常见的需要过滤的情况,一种是对数据进行整条过滤,比如只能查看自己部门的数据;另外一种情况是对数据进行部分过滤,比如某些人不能查看工资数据。

    ?????? 带迭代策略的迭代器实现的一个基本思路,就是先把聚合对象的聚合数据获取到,并存储到迭代器里面来,这样迭代器就可以按照不同的策略来迭代数据了。

    1:带迭代策略的迭代器示例

    沿用上一个例子,来修改ArrayIteratorImpl来简单的示意一下,不去考虑复杂的算法,大致的修改有:

      原来是持有聚合对象的,现在直接把这个聚合对象的内容取出来存放到迭代器里面,也就是迭代的时候,直接在迭代器里面来获取具体的聚合对象的元素,这样才好控制迭代的数据在迭代器的具体实现上加入过滤的功能

      示例代码如下:

      /**

      ?* 用来实现访问数组的迭代接口,加入了迭代策略

      ?*/

      public class ArrayIteratorImpl implements Iterator{

      ??? /**

      ??? ?* 用来存放被迭代的数组

      ??? ?*/

      ??? private PayModel[] pms = null;

      ??? /**

      ??? ?* 用来记录当前迭代到的位置索引

      ??? ?*/

      ??? private int index = 0;

      ?

      ??? public ArrayIteratorImpl(SalaryManager aggregate){

      ?????? //在这里先对聚合对象的数据进行过滤,比如工资必须在3000以下

      ?????? Collection<PayModel> tempCol = new ArrayList<PayModel>();

      ?????? for(PayModel pm : aggregate.getPays()){

      ?????????? if(pm.getPay() < 3000){

      ????????????? tempCol.add(pm);

      ?????????? }

      ?????? }

      ?????? //然后把符合要求的数据存放到用来迭代的数组

      ?????? this.pms = new PayModel[tempCol.size()];

      ?????? int i=0;

      ?????? for(PayModel pm : tempCol){

      ?????????? this.pms[i] = pm;

      ?????????? i++;

      ?????? }

      ??? }

      ??? public boolean hasNext() {

      ?????? //判断是否还有下一个元素

      ?????? if(pms!=null && index<=(pms.length-1)){

      ?????????? return true;

      ?????? }

      ?????? return false;

      ??? }

      ??? public Object next() {

      ?????? Object retObj = null;

      ?????? if(hasNext()){

      ?????????? retObj = pms[index];

      ?????????? //每取走一个值,就把已访问索引加1

      ?????????? index++;

      ?????? }

      ?????? //在这里对要返回的数据进行过滤,比如不让查看工资数据

      ?????? ((PayModel)retObj).setPay(0.0);

      ?

      ?????? return retObj;

      ??? }

      ??? public void remove() {

      ?????? //暂时可以不实现???

      ??? }

      }

      2:谁定义遍历算法的问题

      ?????? 在实现迭代器模式的时候,一个常见的问题就是:谁来定义遍历算法?其实带策略的迭代器讲述的也是这个问题。

      ?????? 在迭代器模式的实现中,常见有两个地方可以来定义遍历算法,一个就是聚合对象本身,另外一个就是迭代器负责遍历算法。

      ?????? 在聚合对象本身定义遍历的算法这种情况下,通常会在遍历过程中,用迭代器来存储当前迭代的状态,这种迭代器被称为游标,因为它仅用来指示当前的位置。比如在14.2.4里面示例的迭代器就属于这种情况。

      ?????? 在迭代器里面定义遍历算法,会易于在相同的聚合上使用不同的迭代算法,同时也易于在不同的聚合上重用相同的算法。比如上面带策略的迭代器的示例,迭代器把需要迭代的数据从聚合对象中取出并存放到自己对象里面,然后再迭代自己的数据,这样一来,除了刚开始创建迭代器的时候需要访问聚合对象外,真正迭代过程已经跟聚合对象无关了。

      ?????? 当然,在迭代器里面定义遍历算法,如果实现遍历算法的时候需要访问聚合对象的私有变量,那么将遍历算法放入迭代器中会破坏聚合对象的封装性。

      ?????? 至于究竟使用哪一种方式,要具体问题具体分析。

      14.3.4? 双向迭代器

      ?????? 所谓双向迭代器的意思就是:可以同时向前和向后遍历数据的迭代器。

      ?????? 在Java util包中的ListIterator接口就是一个双向迭代器的示例,当然自己实现双向迭代器也非常容易,只要在自己的Iterator接口中添加向前的判断和向前获取值的方法,然后在实现中实现即可。

      ?????? 延续前面14.2.4的示例,来自己实现双向迭代器,相同的部分就不去示范了,只演示不同的地方,先看看新的迭代器接口,示例代码如下:

      /**

      ?* 迭代器接口,定义访问和遍历元素的操作,实现双向迭代

      ?*/

      public interface Iterator {

      ??? public void first();

      ??? public void next();

      ??? public boolean isDone();

      ??? public Object currentItem();

      ??? /**

      ??? ?* 判断是否为第一个元素

      ??? ?* @return 如果为第一个元素,返回true,否则返回false

      ??? ?*/

      ??? public boolean isFirst();

      ??? /**

      ??? ?* 移动到聚合对象的上一个位置

      ??? ?*/

      ??? public void previous();

      }

      有了新的迭代器接口,也应该有新的实现。示例代码如下:

      /**

      ?* 用来实现访问数组的双向迭代接口

      ?*/

      public class ArrayIteratorImpl implements Iterator{

      ??? private SalaryManager aggregate = null;

      ??? private int index = -1;?

      ??? public ArrayIteratorImpl(SalaryManager aggregate){

      ?????? this.aggregate = aggregate;

      ??? }

      ??? public void first(){

      ?????? index = 0;

      ??? }

      ??? public void next(){

      ?????? if(index < this.aggregate.size()){

      ?????????? index = index + 1;

      ?????? }

      ??? }

      ??? public boolean isDone(){

      ?????? if(index == this.aggregate.size()){

      ?????????? return true;

      ?????? }

      ?????? return false;

      ??? }

      ??? public Object currentItem(){

      ?????? return this.aggregate.get(index);

      ??? }

      ?

      ????public?boolean isFirst(){

      ?????? if(index==0){

      ?????????? return true;

      ?????? }

      ?????? return false;

      ??? }

      ??? public void previous(){

      ?????? if(index > 0 ){

      ?????????? index = index - 1;

      ?????? }

      ??? }

      }

      基本实现完了,写个客户端来享受一下双向迭代的乐趣。由于这个实现要考虑同时控制向前和向后迭代取值,而控制当前索引的是同一个值,因此在获取向前取值得时候,要先把已访问索引减去1,然后再取值,这个跟向后取值是反过来的,注意一下。示例代码如下:

      public class Client {

      ??? public static void main(String[] args) {

      ?????? //访问新收购公司的工资列表

      ?????? SalaryManager salaryManager = new SalaryManager();

      ?????? //先计算再获取

      ?????? salaryManager.calcSalary();

      ?

      ?????? //得到双向迭代器

      ?????? Iterator it = salaryManager.createIterator();

      ?????? //首先设置迭代器到第一个元素

      ?????? it.first();

      ?

      ?????? //先next一个

      ?????? if(!it.isDone()){

      ?????????? PayModel pm = (PayModel)it.currentItem();

      ?????????? System.out.println("next1 == "+pm);

      ?????????? //向下迭代一个

      ?????????? it.next();

      ?????? }

      ?????? //然后previous一个

      ?????? if(!it.isFirst()){

      ?????????? //向前迭代一个

      ?????????? it.previous();

      ?????????? PayModel pm = (PayModel)it.currentItem();

      ?????????? System.out.println("previous1 == "+pm);?????????

      ?????? }

      ?????? //再next一个

      ?????? if(!it.isDone()){

      ?????????? PayModel pm = (PayModel)it.currentItem();

      ?????????? System.out.println("next2 == "+pm);

      ?????????? //向下迭代一个

      ?????????? it.next();

      ?????? }

      ?????? //继续next一个

      ?????? if(!it.isDone()){

      ?????????? PayModel pm = (PayModel)it.currentItem();

      ?????????? System.out.println("next3 == "+pm);

      ?????????? //向下迭代一个

      ?????????? it.next();

      ?????? }

      ?????? //然后previous一个

      ?????? if(!it.isFirst()){

      ?????????? //向前迭代一个

      ?????????? it.previous();

      ?????????? PayModel pm = (PayModel)it.currentItem();

      ?????????? System.out.println("previous2 == "+pm);?????????

      ?????? }

      ?

      ??? }

      }

      上面的示例故意先向后取值,然后再向前取值,这样反复才能看出双向迭代器的效果。运行的结果如下:

      next1 == userName=王五,pay=2200.0

      previous1 == userName=王五,pay=2200.0

      next2 == userName=王五,pay=2200.0

      next3 == userName=赵六,pay=3600.0

      previous2 == userName=赵六,pay=3600.0

      可能有些人会疑惑:为什么next1和previous1取出来的值是一样的呢?

      这是因为现在是顺序迭代,当next显示第一条的时候,内部索引已经指向第二条了,所以这个时候再previous向前一条的时候,数据就是第一条数据了。

      再仔细看上面的结果,发现这个时候继续next数据时,数据还是第一条数据,同理,刚才previous向前一条的时候,内部索引已经指向第一条之前了。

      14.3.5? 迭代器模式的优缺点

      /**

      ?* 定义翻页访问聚合元素的迭代接口

      ?*/

      public interface AggregationIterator {

      ??? /**

      ??? ?* 判断是否还有下一个元素,无所谓是否够一页的数据,

      ??? ?* 因为最后哪怕只有一条数据,也是要算一页的

      ??? ?* @return 如果有下一个元素,返回true,没有下一个元素就返回false

      ??? ?*/

      ??? public boolean hasNext();

      ??? /**

      ??? ?* 取出下面几个元素

      ??? ?* @param num 需要获取的记录条数

      ??? ?* @return 下面几个元素

      ??? ?*/

      ??? public Collection next(int num);

      ??? /**

      ??? ?* 判断是否还有上一个元素,无所谓是否够一页的数据,

      ??? ?* 因为最后哪怕只有一条数据,也是要算一页的

      ??? ?* @return 如果有上一个元素,返回true,没有上一个元素就返回false

      ??? ?*/

      ??? public boolean hasPrevious();

      ??? /**

      ??? ?* 取出上面几个元素

      ??? ?* @param num 需要获取的记录条数

      ??? ?* @return 上面几个元素

      ??? ?*/

      ??? public Collection previous(int num);

      }

      (2)PayModel跟前面的示例是一样的,这里就不去赘述了

      (3)接下来看看SalaryManager的实现,有如下改变:

        不用再实现获取聚合对象大小和根据索引获取聚合对象中的元素的功能了在准备测试数据的时候,多准备几条,好看出翻页的效果

        示例代码如下:

        /**

        ?* 被客户方收购的那个公司的工资管理类

        ?*/

        public class SalaryManager{

        ??? private PayModel[] pms = null;

        ??? public PayModel[] getPays(){

        ?????? return pms;

        ??? }

        ??? public void calcSalary(){

        ?????? //计算工资,并把工资信息填充到工资列表里面

        ?????? //为了测试,做点数据进去

        ?????? PayModel pm1 = new PayModel();

        ?????? pm1.setPay(2200);

        ?????? pm1.setUserName("王五");

        ?

        ?????? PayModel pm2 = new PayModel();

        ?????? pm2.setPay(3600);

        ?????? pm2.setUserName("赵六");

        ?

        ?????? PayModel pm3 = new PayModel();

        ?????? pm3.setPay(2200);

        ?????? pm3.setUserName("王五二号");

        ?

        ?????? PayModel pm4 = new PayModel();

        ?????? pm4.setPay(3600);

        ?????? pm4.setUserName("赵六二号");

        ?

        ?????? PayModel pm5 = new PayModel();

        ?????? pm5.setPay(2200);

        ?????? pm5.setUserName("王五三号");

        ?

        ?????? pms = new PayModel[5];

        ?????? pms[0] = pm1;

        ?????? pms[1] = pm2;

        ?????? pms[2] = pm3;

        ?????? pms[3] = pm4;

        ?????? pms[4] = pm5;

        ??? }

        ??? public AggregationIterator createIterator() {

        ?????? return new ArrayIteratorImpl(this);

        ??? }

        }

        (4)然后再看看如何实现迭代器接口,示例代码如下:

        /**

        * 用来实现翻页访问聚合元素的迭代接口
        */

        public class ArrayIteratorImpl implements AggregationIterator{

        ??? /**

        * 用来存放被迭代的数组

        */

        ??? private PayModel[] pms = null;

        ??? /**

        * 用来记录当前迭代到的位置索引

        */

        ??? private int index = 0;

        ??? public ArrayIteratorImpl(SalaryManager aggregate){

        ?????? this.pms = aggregate.getPays();

        ??? }

        ??? public boolean hasNext() {

        ?????? //判断是否还有下一个元素

        ?????? if(pms!=null && index<=(pms.length-1)){

        ?????????? return true;

        ?????? }

        ?????? return false;

        ??? }

        public boolean hasPrevious() {

        ?????? if(pms!=null && index > 0){

        ?????????? return true;

        ?????? }

        ?????? return false;

        ??? }

        ????public?Collection next(int num) {

        ?????? Collection col = new ArrayList();

        ?????? int count=0;

        ?????? while(hasNext() && count<num){

        ?????????? col.add(pms[index]);

        ?????????? //每取走一个值,就把已访问索引加1

        ?????????? index++;

        ?????????? count++;

        ?????? }

        ?????? return col;

        ??? }

        ????public?Collection previous(int num){

        ?????? Collection col = new ArrayList();

        ?????? int count=0;

        ?????? //简单的实现就是把索引退回去num个,然后再取值。

        ?????? //但事实上这种实现是有可能多退回去数据的,比如:已经到了最后一页,

        //而且最后一页的数据不够一页的数据,那么退回去num个索引就退多了

        ?????? //为了示例的简洁性,这里就不去处理了

        ?????? index = index - num;

        ?????? while(hasPrevious() && count<num){

        ?????????? col.add(pms[index]);

        ?????????? index ++;

        ?????????? count++;

        ?????? }

        ?????? return col;

        ??? }

        }

        (5)写个客户端测试看看,示例代码如下:

        public class Client {

        ??? public static void main(String[] args) {

        ?????? //访问新收购公司的工资列表,先计算再获取

        ?????? SalaryManager salaryManager = new SalaryManager();

        ?????? salaryManager.calcSalary();

        ?????? //得到翻页迭代器

        ?????? AggregationIterator it = salaryManager.createIterator();

        ?

        ?????? //获取第一页,每页显示2条

        ?????? Collection col = it.next(2);

        ?????? System.out.println("第一页数据:");

        ???????print(col);

        ?????? //获取第二页,每页显示2条

        ?????? Collection col2 = it.next(2);

        ?????? System.out.println("第二页数据:");

        ???????print(col2);

        ?

        ?????? //向前一页,也就是再次获取第二页

        ?????? Collection col3 = it.previous(2);

        ?????? System.out.println("再次获取第二页数据:");

        ???????print(col3);

        ??? }

        ??? private static void print(Collection col){

        ?????? Iterator it = col.iterator();

        ?????? while(it.hasNext()){

        ?????????? Object obj = it.next();

        ?????????? System.out.println(obj);

        ?????? }

        ??? }

        }

        运行的结果如下:

        第一页数据:

        userName=王五,pay=2200.0

        userName=赵六,pay=3600.0

        第二页数据:

        userName=王五二号,pay=2200.0

        userName=赵六二号,pay=3600.0

        再次获取第二页数据:

        userName=王五二号,pay=2200.0

        userName=赵六二号,pay=3600.0

        仍然是顺序迭代的,也就是获取完第二页数据,内部索引就指向后面了,这个时候再运行向前一页,取的就还是第二页的数据了。

        2:随机翻页迭代器示例

        估计看到这里,有些朋友会想,实际应用中,用户怎么会这么老实,按照顺序访问,通常情况都是随意的访问页数,比如看了第一页可能就直接点第三页了,看完第三页他又想看第一页。

        这就需要随机翻页迭代器了,也就是可以指定页面号和每页显示的数据来访问数据的迭代器,下面来看看示例。

        (1)修改迭代接口的方法,不需要再有向前和向后的方法,取而代之的是指定页面号和每页显示的数据来访问的方法,示例代码如下:

        /**

        ?* 定义随机翻页访问聚合元素的迭代接口

        ?*/

        public interface AggregationIterator {

        ??? /**

        ??? ?* 判断是否还有下一个元素,无所谓是否够一页的数据,

        ??? ?* 因为最后哪怕只有一条数据,也是要算一页的

        ??? ?* @return 如果有下一个元素,返回true,没有下一个元素就返回false

        ??? ?*/

        ??? public boolean hasNext();??

        ??? /**

        ??? ?* 判断是否还有上一个元素,无所谓是否够一页的数据,

        ??? ?* 因为最后哪怕只有一条数据,也是要算一页的

        ??? ?* @return 如果有上一个元素,返回true,没有上一个元素就返回false

        ??? ?*/

        ??? public boolean hasPrevious();

        ??? /**

        ??? ?* 取出指定页数的数据

        ??? ?* @param pageNum 要获取的页数

        ??? ?* @param pageShow 每页显示的数据条数

        ??? ?* @return 指定页数的数据

        ??? ?*/

        ????public?Collection getPage(int pageNum,int pageShow);

        }

        (2)定义了接口,看看具体的实现,示例代码如下:

        /**

        ?* 用来实现随机翻页访问聚合元素的迭代接口

        ?*/

        public class ArrayIteratorImpl implements AggregationIterator{

        ??? /**

        ??? ?* 用来存放被迭代的数组

        ??? ?*/

        ??? private PayModel[] pms = null;

        ??? /**

        ??? ?* 用来记录当前迭代到的位置索引

        ??? ?*/

        ??? private int index = 0;??

        ??? public ArrayIteratorImpl(SalaryManager aggregate){

        ?????? this.pms = aggregate.getPays();

        ??? }

        ??? public boolean hasNext() {

        ?????? //判断是否还有下一个元素

        ?????? if(pms!=null && index<=(pms.length-1)){

        ?????????? return true;

        ?????? }

        ?????? return false;

        ??? }

        ??? public boolean hasPrevious() {

        ?????? if(pms!=null && index > 0){

        ?????????? return true;

        ?????? }

        ?????? return false;

        ??? }

        ????public?Collection getPage(int pageNum,int pageShow){

        ?????? Collection col = new ArrayList();

        ?????? //需要在这里先计算需要获取的数据的开始条数和结束条数

        ?????? int start = (pageNum-1)*pageShow;

        ?????? int end = start + pageShow-1;

        ?????? //控制start的边界,最小是0

        ?????? if(start < 0){

        ?????????? start = 0;

        ?????? }

        ?????? //控制end的边界,最大是数组的最大索引

        ?????? if(end > this.pms.length-1){

        ?????????? end = this.pms.length - 1;

        ?????? }

        ?????? //每次取值都是从头开始循环,所以设置index0

        ?????? index = 0;

        ?????? while(hasNext()? && index<=end){

        ?????????? if(index >= start){

        ????????????? col.add(pms[index]);

        ?????????? }

        ?????????? //把已访问索引加1

        ?????????? index++;

        ?????? }

        ?????? return col;

        ??? }

        }

        (3)写个客户端,测试看看,是否能实现随机的翻页,示例代码如下:

        public class Client {

        ??? public static void main(String[] args) {

        ?????? //访问新收购公司的工资列表

        ?????? SalaryManager salaryManager = new SalaryManager();

        ?????? //先计算再获取

        ?????? salaryManager.calcSalary();

        ?????? //得到翻页迭代器

        ?????? AggregationIterator it = salaryManager.createIterator();

        ?

        ?????? //获取第一页,每页显示2条

        ?????? Collection col = it.getPage(1,2);

        ?????? System.out.println("第一页数据:");

        ???????print(col);

        ?????? //获取第二页,每页显示2条

        ?????? Collection col2 = it.getPage(2,2);

        ?????? System.out.println("第二页数据:");

        ???????print(col2);

        ?????? //再次获取第一页

        ?????? Collection col3 = it.getPage(1,2);

        ?????? System.out.println("再次获取第一页数据:");

        ???????print(col3);

        ?????? //获取第三页

        ?????? Collection col4 = it.getPage(3,2);

        ?????? System.out.println("获取第三页数据:");

        ???????print(col4);

        ??? }

        ??? private static void print(Collection col){

        ?????? Iterator it = col.iterator();

        ?????? while(it.hasNext()){

        ?????????? Object obj = it.next();

        ?????????? System.out.println(obj);

        ?????? }?????

        ??? }

        }

        测试的结果如下:

        第一页数据:

        userName=王五,pay=2200.0

        userName=赵六,pay=3600.0

        第二页数据:

        userName=王五二号,pay=2200.0

        userName=赵六二号,pay=3600.0

        再次获取第一页数据:

        userName=王五,pay=2200.0

        userName=赵六,pay=3600.0

        获取第三页数据:

        userName=王五三号,pay=2200.0

        14.3.8? 相关模式

        l????????? 迭代器模式和工厂方法模式
        ??? 这两个模式可以组合使用。
        ??? 在聚合对象创建迭代器的时候,通常会采用工厂方法模式来实例化相应的迭代器对象。

        ?

        ---------------------------------------

        私塾在线学习网原创内容 ?跟着cc学设计系列 之?研磨设计模式

        研磨设计讨论群【252780326】

        原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/0/5335.html】

        ---------------------------------------

热点排行