迭代器模式是将对对象的遍历封装成迭代器,对外部隐藏对象遍历的具体实现。
而组合方法是实现对处于不同层次的同种对象提供一致性的遍历方法。
首先我们开始讨论迭代器模式。
我们在实践过程中可以遇到这样的情况,我们需要对用array,ArrayList,Stack 等模式包装的一组对象进行遍历
public class Item{ ......... } public class Demo1{ Item[] items; } public class Demo2{ List<Item> items; }
就如上面程序所示,我们需要堆Demo1 和Demo2的items 对象进行遍历操作,1种很常见的方式是对Demo1和Demo2的对象的类型提供不同的遍历方式。
for(int i = 0 ; i < demo1.items.length;i++){.....} for(int i = 0 ; i < demo2.items.size();i++){.....}
(先无视这里违反的最小知识原则) 这样的实现是明显有问题,如果我们存在额外的对象类型比如生活stack ,那么我们仍然需要重新按照这种方法修改源程序,如果我们存在多个不同类型的对象,我们需要按照对象的类型对他们进行分类然后对他们分别进行遍历,这样的实现,一方面存在很多冗余代码,另外一方面违反开闭原则。
迭代器模式针对这样的情况提供了这样的实现方式。
public class Item{ ......... } public interface GetItemIterator{ public Iterator creatIterator(); } public class ArrayIterator implements Iterator{ ..... } public class Demo1 implements GetItemIterator{ Item[] items; public Iterator creatIterator(){ return new ArrayIterator(items); } } public class Demo2 implements GetItemIterator{ List<Item> items; public Iterator creatIterator(){ return items.Iterator();} }
我们对于每一个需要提供对象遍历的类实现一个创建迭代器的方法,在主程序中 我们对具体对象的遍历可以通过迭代器实现,在主程序中对对个Demo1 Demo2对象访问的代码也可以合并,去除了冗余的代码。
使用迭代器模式最大的好处就是对外部隐藏了内部对象遍历的具体实现, 如果所有遍历都通过迭代器遍历,那么可以消除遍历对象时产生的冗余代码。
接下来让我们讨论组合方法模式。
组合方法让我想起了之前在操作系统课中实现对文件目录系统访问的程序。
文件目录是通过树的方式实现,目录区的每条记录可以被称作目录项,如果目录项指向文件,那么目录项中的存的跳转地址就是文件头的地址,如果目录项是一个目录,那么他的跳转地址指向的区域是一个需要被当做目录区访问的空间。
我当初给出的实现方法是顺序读入根目录下的目录项,遇到文件,直接出栈文件名和文件地址,遇到目录,输出目录名,然后用递归读取该目录指向的目录区的目录内容。
这个也是组合方法的一个实现方法。
组合方法的目的是使客户用一致的方法去处理对象和子对象
组合方法存在这样的关系, 其中Leaf 指的是标准叶节点,在文件系统中对应的就是文件,而Compoiste可以包含0个或多个其他的Composite和0个或多个Leaf,在文件系统中对应的就是目录,在Component是两者的统一接口,在文件系统中对应的是目录项,注意两者的作用不同,目录项是为了标志类型,而component是提供统一实现的接口,简单说就是一个Component使得程序与具体实现类解耦。
下面是代码
public abstract class CatalogueItem{ public String getItemName throws Exception{ throw new Exception("不支持");} public String getItemLocation throws Exception{ throw new Exception("不支持");} public Iterator getSubItems throws Exception{ throw new Exception("不支持");} } public class Catalogue extends CatalogueItem{ public String getItemName throws Exception{ return name;} public String getItemLocation throws Exception{return location;} public Iterator getSubItems throws Exception{ return new ItemIterator(items)} } public class FileItem extends CatalogueItem{ public String getItemName throws Exception{ return name;} public String getItemLocation throws Exception{return location;} public Iterator getSubItems throws Exception{ return new NullIterator();} } //空迭代器 public class NullIterator implements Iterator{ public boolean hasNext() { return false; } public Object next() { return null; } public void remove() { System.out.println("不支持remove操作");} } //普通迭代器 public class ItemIterator implements Iterator { Stack stack = new Stack<Iterator>(); public ItemIterator(Iterator params) { stack.push(params); } public boolean hasNext() { if (stack.size() > 0) { Iterator temp = (Iterator) stack.peek(); if (temp.hasNext()) { return true; } else { stack.pop(); return hasNext(); } } else { return false; } } public Object next() { Item reval = (Item) ((Iterator) stack.peek()).next(); try { stack.push(reval.getSubItems()); } catch (Exception e) { } return reval; } public void remove() { System.out.println("不支持remove操作"); } }
组合模式下 类对外提供了同一的迭代器接口,而隐藏了内在的层次性结构, 这样的实现与我之前用的那种方法的区别在于是否使用instanceof来判定类别并给与不同的操作。
组合模式的问题在于提供统一的接口,于是某些不需要的方法被一些类继承了,我们基本是使用异常的方式处理掉,另外由于层次性结构被隐藏了,所以我们不能对目录进行缩进显示。
instanceof 方法的好处是可以使用递归的方法提供更加细致的处理,但是违反了开闭的原则,如果出现修改需求,instanceof带来的修改负担很大