组合模式允许将对象组合成树形结构来表示“整体与部分”的层次结构,能让给客户以一致的方式来处理个别对象以及对象组合。
以菜单为例思考,这个模式可以创建一个树形结构,在同一个结构中处理嵌套菜单和菜单项组,菜单包含子菜单,子菜单下分为菜单项,任何一个菜单都是一种组合,它既可以包含其他菜单,也可以包含菜单项。
菜单类和菜单项类继承同一个父类,继承的方法不同,但是我们可以忽略个别对象和对象组合的区别。
组合模式的类图如下:
下面我们结合迭代器模式和组合模式,实现遍历打印菜单,具体类图如下:
MenuComponent类
import java.util.Iterator;/** 菜单组件类,菜单项和菜单都要继承的父类。**/public abstract class MenuComponent{ public void add(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public MenuComponent getChild(int i) { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getPrice() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } public Iterator createIterator() { throw new UnsupportedOperationException(); } }
继承了MenuComponent类的Menu类
/**菜单组件类,菜单项和菜单都要继承的父类。**/public class Menu extends MenuComponent{ ArrayListmenuComponents; String name; public Menu(String name) { this.name=name; menuComponents=new ArrayList (); } public void add(MenuComponent menuComponent) { menuComponents.add(menuComponent); } public void remove(MenuComponent menuComponent) { menuComponents.remove(menuComponent); } public MenuComponent getChild(int i) { return menuComponents.get(i); } public String getName() { return name; } public void print() { System.out.println("Menu:"+name); System.out.println("-----------------"); Iterator iterator=createIterator(); while(iterator.hasNext()) { MenuComponent menuComponent=(MenuComponent)iterator.next(); menuComponent.print(); } } public Iterator createIterator() { return new CompositeIterator(menuComponents.iterator()); }}
继承了MenuComponent类的MenuItem类,相当于菜单项
import java.util.Iterator;/** 菜单项类,继承了菜单组件类**/public class MenuItem extends MenuComponent{ String name; String price; public MenuItem(String name,String price) { this.name=name; this.price=price; } public String getName() { return name; } public String getPrice() { return price; } public void print() { System.out.println(name+":"+price); } public Iterator createIterator() { return new NullIterator(); } }
CompositeIterator类,作为菜单的迭代器
import java.util.*;public class CompositeIterator implements Iterator{ Stack stack; public CompositeIterator(Iterator iterator) { stack=new Stack(); stack.push(iterator); } public Object next() { if(hasNext()) { Iterator iterator=(Iterator)stack.peek(); MenuComponent menuComponent=(MenuComponent)iterator.next(); if(menuComponent instanceof Menu) { stack.push(menuComponent.createIterator()); } return menuComponent; } else return null; } public boolean hasNext() { if(stack.isEmpty()) { return false; } else { Iterator iterator=(Iterator)stack.peek(); if(!iterator.hasNext()) { stack.pop(); return hasNext(); } else { return true; } } } public void remove() { throw new UnsupportedOperationException(); }}
NullIterator类,作为菜单项的迭代器
import java.util.Iterator;public class NullIterator implements Iterator{ public Object next() { return null; } public boolean hasNext() { return false; } public void remove() { throw new UnsupportedOperationException(); }}
Waitress类,大大降低了Waitress类与菜单之间的耦合度,Waitress类只需要知道根节点就可以了。
import java.util.Iterator;/**Client**/public class Waitress{ MenuComponent menus; public Waitress(MenuComponent menuComponent) { this.menus=menuComponent; } public void printMenu() { menus.print(); }}
测试类
package com.qingfei.composite;public class Test{ public static void main(String[] args) { MenuComponent menus=new Menu("all Menu"); MenuComponent dinerMenu=new Menu("Diner Menu"); MenuComponent pancakeMenu=new Menu("Pancake Menu"); MenuComponent cafeMenu=new Menu("cafe Menu"); menus.add(dinerMenu); menus.add(pancakeMenu); dinerMenu.add(new MenuItem("noodle","10")); dinerMenu.add(new MenuItem("rice","20")); dinerMenu.add(new MenuItem("soup","5")); cafeMenu.add(new MenuItem("latte","30")); cafeMenu.add(new MenuItem("mocha","25")); pancakeMenu.add(new MenuItem("pizza","50")); pancakeMenu.add(new MenuItem("cheese burger","20")); pancakeMenu.add(cafeMenu); Waitress service=new Waitress(menus); service.printMenu(); }}
结果是
在组合模式中,客户并不需要知道具体有哪些类,只需要知道一个接口,不需要知道哪些是父对象哪些是子对象,也就不需要写大量的判断语句了。