ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
第20章 迭代器模式 20.1 整理项目信息——苦差事 周五下午,我正在看技术网站,第六感官发觉有人在身后,扭头一看,老大站在背后,我赶忙站起来。 “王经理,你找我?” “哦,在看技术呀。有个事情找你谈一下,你到我办公室来一下。” 到老大办公室还没坐稳,老大就开始发话了。 “是这样,刚刚我在看季报,我们每个项目的支出费用都很高,项目情况复杂,人员情况也不简单,我看着也有点糊涂,你看,这是我们现在还在开发或者维护的103个项目,项目信息很乱,很多是两年前的信息,你能不能先把这些项目最新情况重新打印一份给我,咱们好查查到底有什么问题。”老大说。 “这个好办,我马上去办!”我爽快地答复道。 很快我设计了一个类图,准备实施,如图20-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b00366b62df.jpg) 图20-1 项目信息类图 简单得不能再简单的类图,是个程序员都能实现。我们来看看这个简单的东西,先看接口,如代码清单20-1所示。 代码清单20-1 项目信息接口 public interface IProject {      //从老板这里看到的就是项目信息      public String getProjectInfo(); } 定义了一个接口,面向接口编程嘛,当然要定义接口了,然后看看实现类,如代码清单20-2所示。 代码清单20-2 项目信息的实现 public class Project implements IProject {      //项目名称      private String name = "";      //项目成员数量      private int num = 0;      //项目费用      private int cost = 0;      //定义一个构造函数,把所有老板需要看到的信息存储起来      public Project(String name,int num,int cost){              //赋值到类的成员变量中              this.name = name;              this.num = num;              this.cost=cost;      }      //得到项目的信息      public String getProjectInfo() {              String info = "";              //获得项目的名称              info = info+ "项目名称是:" + this.name;              //获得项目人数              info = info + "\t项目人数: "+ this.num;              //项目费用              info = info+ "\t 项目费用:"+ this.cost;              return info;      } } 实现类也是极度简单,通过构造函数把要显示的数据传递过来,然后放到getProjectInfo中显示,这太容易了!然后我们老大要看看结果了,如代码清单20-3所示。 代码清单20-3 老大看报表的场景 public class Boss {              public static void main(String[] args) {                      //定义一个List,存放所有的项目对象                      ArrayList<IProject> projectList = new ArrayList<IProject>();                      //增加星球大战项目                      projectList.add(new Project("星球大战项目",10,100000));                      //增加扭转时空项目                      projectList.add(new Project("扭转时空项目",100,10000000));                      //增加超人改造项目                      projectList.add(new Project("超人改造项目",10000,1000000000));                      //这边100个项目                      for(int i=4;i<104;i++){                              projectList.add(new Project("第"+i+"个项目",i*5,i*1000000));                      }                      //遍历一下ArrayList,把所有的数据都取出                      for(IProject project:projectList){              System.out.println(project.getProjectInfo());                                         }              } } 然后看一下我们的运行结果,如下所示: 项目名称是:星球大战项目  项目人数: 10     项目费用:100000 项目名称是:扭转时空项目  项目人数: 100     项目费用:10000000 项目名称是:超人改造项目  项目人数: 10000   项目费用:1000000000 项目名称是:第4个项目   项目人数: 20     项目费用:4000000 项目名称是:第5个项目   项目人数: 25      项目费用:5000000 . . . 老大一看,非常开心,这么快就出结果了,大大地把我夸奖了一番,然后就去埋头研究那堆枯燥的报表了。我回到座位上,又看了一遍程序(心里很乐,就又想看看自己的成果),想想(一日三省嘛),应该还有另外一种实现方式,因为是遍历嘛,让我想到的就是Java的迭代器接口java.util.iterator,它的作用就是遍历Collection集合下的元素,那我们的程序还可以有另外一种实现,通过实现iterator接口来实现遍历,先修正一下类图,如图20-2所示。 ![](https://box.kancloud.cn/2016-08-14_57b00366d08f4.jpg) 图20-2 增加迭代接口的类图 看着是不是复杂了很多?是的,是有点复杂了,是不是我们把简单的事情复杂化了?请读者继续阅读下去,我等会儿说明原因。我们先分析一下我们的类图java.util.Iterator接口中声明了三个方法,这是JDK定义的, ProjectIterator 实现该接口,并且聚合了Project对象,也就是把Project对象作为本对象的成员变量使用。看类图还不是很清晰,我们一起看一下代码,先看IProject接口的改变,如代码清单20-4所示。 代码清单20-4 项目信息接口 public interface IProject {      //增加项目      public void add(String name,int num,int cost);      //从老板这里看到的就是项目信息      public String getProjectInfo();      //获得一个可以被遍历的对象      public IProjectIterator iterator(); } 这里多了两个方法,一个是add方法,这个方法是增加项目,也就是说产生了一个对象后,直接使用add方法增加项目信息。我们再来看其实现类,如代码清单20-5所示。 代码清单20-5 项目信息 public class Project implements IProject {      //定义一个项目列表,说有的项目都放在这里      private ArrayList<IProject> projectList = new ArrayList<IProject>();      //项目名称      private String name = "";      //项目成员数量      private int num = 0;      //项目费用      private int cost = 0;      public Project(){            }      //定义一个构造函数,把所有老板需要看到的信息存储起来      private Project(String name,int num,int cost){              //赋值到类的成员变量中              this.name = name;              this.num = num;              this.cost=cost;      }      //增加项目      public void add(String name,int num,int cost){              this.projectList.add(new Project(name,num,cost));      }      //得到项目的信息      public String getProjectInfo() {              String info = "";              //获得项目的名称              info = info+ "项目名称是:" + this.name;              //获得项目人数              info = info + "\t项目人数: "+ this.num;              //项目费用              info = info+ "\t 项目费用:"+ this.cost;              return info;      }      //产生一个遍历对象      public IProjectIterator iterator(){              return new ProjectIterator(this.projectList);      } } 通过构造函数,传递了一个项目所必需的信息,然后通过iterator()方法,把所有项目都返回到一个迭代器中。Iterator()方法看不懂不要紧,继续向下阅读。再看IProjectIterator接口,如代码清单20-6所示。 代码清单20-6 项目迭代器接口 public interface IProjectIterator extends Iterator { } 大家可能对该接口感觉很奇怪,你定义的这个接口方法、变量都没有,有什么意义呢?有意义,所有的Java书上都会说要面向接口编程,你的接口是对一个事物的描述,也就是说我通过接口就知道这个事物有哪些方法,哪些属性,我们这里的IProjectIterator是要建立一个指向Project类的迭代器,目前暂时定义的就是一个通用的迭代器,可能以后会增加IProjectIterator的一些属性或者方法。当然了,你也可以在实现类上实现两个接口,一个是Iterator,一个是IProjectIterator(这时候,这个接口就不用继承Iterator),杀猪杀尾巴,各有各的杀法。我的习惯是:如果我要实现一个容器或者其他API提供接口时,我一般都自己先写一个接口继承,然后再继承自己写的接口,保证自己的实现类只用实现自己写的接口(接口传递,当然也要实现顶层的接口),程序阅读也清晰一些。我们继续看迭代器的实现类,如代码清单20-7所示。 代码清单20-7 项目迭代器 public class ProjectIterator implements IProjectIterator {      //所有的项目都放在ArrayList中      private ArrayList<IProject> projectList = new ArrayList<IProject>();      private int currentItem = 0;       //构造函数传入projectList      public ProjectIterator(ArrayList<IProject> projectList){              this.projectList = projectList;      }      //判断是否还有元素,必须实现      public boolean hasNext() {              //定义一个返回值              boolean b = true;              if(this.currentItem>=projectList.size()||this.projectList.get(this.currentItem)==null){                   b =false;           }              return b;      }      //取得下一个值      public IProject next() {              return (IProject)this.projectList.get(this.currentItem++);      }      //删除一个对象      public void remove() {              //暂时没有使用到      } } 细心的读者可能会从代码中发现一个问题,java.util.iterator接口中定义next()方法的返回值类型是E,而你在ProjectIterator中返回值却是IProject,E和IProject有什么关系? E是JDK 1.5中定义的新类型:元素(Element),是一个泛型符号,表示一个类型,具体什么类型是在实现或运行时决定,总之它代表的是一种类型,你在这个实现类中把它定义为ProjectIterator,在另外一个实现类可以把它定义为String,都没有问题。它与Object这个类可是不同的,Object是所有类的父类,随便一个类你都可以把它向上转型到Object类,也只是因为它是所有类的父类,它才是一个通用类,而E是一个符号,代表所有的类,当然也代表Object了。 都写完毕了,看看我们的Boss类有多少改动,如代码清单20-8所示。 代码清单20-8 老板看报表 public class Boss {              public static void main(String[] args) {                      //定义一个List,存放所有的项目对象                      IProject project = new Project();                      //增加星球大战项目                      project.add("星球大战项目ddddd",10,100000);                      //增加扭转时空项目                      project.add("扭转时空项目",100,10000000);                      //增加超人改造项目                      project.add("超人改造项目",10000,1000000000);                      //这边100个项目                      for(int i=4;i<104;i++){                              project.add("第"+i+"个项目",i*5,i*1000000);                      }                      //遍历一下ArrayList,把所有的数据都取出                      IProjectIterator projectIterator = project.iterator();                      while(projectIterator.hasNext()){                              IProject p = (IProject)projectIterator.next();                              System.out.println(p.getProjectInfo());                      }              } } 运行结果如下所示: 项目名称是:星球大战项目  项目人数: 10  项目费用:100000 项目名称是:扭转时空项目  项目人数: 100  项目费用:10000000 项目名称是:超人改造项目  项目人数: 10000  项目费用:1000000000 项目名称是:第4个项目  项目人数: 20  项目费用:4000000 项目名称是:第5个项目  项目人数: 25  项目费用:5000000 . . . 运行结果完全相同,但是上面的程序复杂性增加了不少,难道我们退回到原始时代了吗?非也,非也,只是我们回退到JDK 1.0.8版本的编程时代了,我们使用一种新的设计模式——迭代器模式。