考虑这么一个场景:
需要某种树形结构来容纳菜单、子菜单、菜单项;
并能在每个菜单的每个项之间游走;
Composite Pattern (又叫 部分-整体模式,Part-Whole),
composite objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
将对象组合成树形结构来表现“部分-整体”的层次结构,使客户以一致的方式来处理单个对象和组合对象。
public abstract class MenuComponent { /******以下是“组合”方法********/ //默认运行时异常,如果子类不重写 则表示不支持这个操作 public void add(MenuComonent comp) { throw new UnsopportOperationException();} public void remove(MenuComponent comp) { } public MenuComponent getChild(int i) { } /******以下是“操作”方法********/ public String getName() { } public String getPrice() { } public void print() { } }需要一个抽象组件来作为叶子和树枝的共同接口(或父类),客户端不用知道具体的某个节点到底是叶子还是树枝。
public class MenuItem extends MenuComponent { /****叶子支持的操作:重写****/ public String getName() {return name; } public void print() { ...//打印名称、价格等 } /****叶子不支持的操作:不重写*****/ // add() // remove() // public MenuComponent getChild(int i) {} }
public class Menu extends MenuComponent { List<MenuComponent> children = new ArrayList<MenuComponent>(); public String getName() {return name; } public void print() { ...//打印名称等 //接着打印子节点信息 Iterator iter = children.iterator(); while (iter.hasNext()) { MenuComponent comp = iter.next(); comp.print(); //递归 } } public add(MenuComponent comp){ children.add(comp); } public remove(MenuComponent comp) { children.remove(comp); } public MenuComponent getChild(int i) { return children.get(i); } }
上面讲的是“透明版本”的组合模式。
安全版本:
在抽象组件中,只包含操作方法,不包含组合方法;或者让叶子节点、树枝节点分别实现不同的接口。可以防止用户在叶子节点上调用getChild()这种不被支持的操作。
缺点:用户必须首先检查节点类型,然后才能进行方法调用。
上例中的print()方法,通过递归已经实现了对树的遍历。但是比较单一。
Q:要求对已知的某个节点,找到他的所有上级、下级
A:可以在Component中增加一个指向parent的引用
更具有扩展性的解决办法是,将组合模式、迭代器模式结合起来:
1、在抽象组件中增加createIterator()方法
2、叶子组件中,实现createIterator(),返回一个NullIterator,其hasNext()总是false
3、树枝组件中,实现createIterator(),返回一个CompositeIterator
//叶子组件返回NullIterator public class NullIterator implements Iterator { public Object next() { return null;} public boolean hasNext() { return false; } public void remove() {throw new UnsupportedOperationException(); } }
//树枝组件返回CompositeIterator public class CompositeIterator implements Iterator { Stack stack = new Stack(); //何用?存放组件的迭代器 public CompositeIterator(Iterator iter) { stack.push(iter); } public boolean hasNext(){ //堆栈中无迭代器,则表示没有下一个元素 if (stack.empty) { return false; } else { //从stack中取出迭代器 Iterator iter = stack.peek(); //如果该迭代器中中没有下一个元素,则将其弹出堆栈,判断下一个迭代器 if (! iter.hasNext()) { stack.pop(); hasNext(); } //如果该迭代器中有下一个元素,返回true else { return true; } } } public Object next(){ if (hasNext()){ //如果有下一个元素,则直接取出该元素 Iterator iter = stack.peek(); MenuComponent comp = iter.next(); //如果该元素是一个菜单,则我们就有了另一个需要被包含进遍历中的组合,丢进堆栈中 if (comp instanceof Menu) { stack.push(comp.createIterator()); } return comp; } else { return null; } } }