提示:此篇文章与上一篇文章耦合度较高,请先
观看我
合并的新店发现了新的问题
新店老板找到小明他们,说之前的方式的确让我们的服务员减少不少的工作,但是我们现在的各种菜单越来越多,每家一种菜单,就必须在服务员里添加一次,最关键的是,我们准备在某些菜单中加入一些子菜单,就是说菜单里除了菜品,可能还有菜单。
小明看了看主管,疑惑的问这样可以实现吗?主管说,当然可以,我们会用到一种新的模式,用了这种模式,新店遇到的两个问题都解决了!服务员只用知道一个菜单,而这个菜单里面既有菜品又有子菜单,小明说,那这不就成了一个树状结构的菜单了么,菜品属于叶节点,而子菜单属于组合节点,组合节点又可以出现新的叶节点和组合节点……
主管说,对,我们就是要实现这样一种结构,它的名字叫组合模式,我们现在来看看它强大的威力吧!
首先,我们需要定义一个抽象类,它是我们叶节点和组合节点的共同接口:
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 getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
实现具体的一个菜品,也就是我们的也叶节点:
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name,
String description,
boolean vegetarian,
double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
我们可以看到,对于菜品类,并没覆盖父类的add、remove、getChild方法,因为对于叶节点,它在没有向下的叶节点和组合节点了。
实现菜单项,也就是我们的组合节点,它包含了叶节点和组合节点,也就是又有菜品,又有子菜单:
public class Menu extends MenuComponent {
ArrayList menuComponents = new ArrayList();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
menuComponent.print();
}
}
}
和上面一样,对于父类的一些方法,我们的菜单项也没有实现的,因为对于菜单项那些方法是没有意义。再比较一下print()方法,上面的叶节点,它是以明确的菜品,我们可以打印出它的一些信息,而对于组合节点,由于它只是一个菜单,我们要打印的不光是它的信息,我们还需要它里面包含的菜品,当然还有它里面包含的子菜单的菜品,和子菜单中子菜单的菜品……于是我们使用迭代器遍历它,然后再调用里面包含的菜单信息的打印方法(可能是菜单、也可能是菜品,如果是菜单会有递归)。
现在,服务员轻松了:
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
}
定义
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
定义组合模式的迭代器
在组合模式中,定义一个迭代器似乎是有必要的,因为组合节点是一个集合,所以我们需要遍历它,在上面的实现中,我们已经使用了迭代器来打印,他只是在组合节点中来使用迭代来遍历组合节点中的集合,现在我们需要一个迭代器来访问组合模式中的所有元素,首先我们需要在叶节点和组合节点的实现类中加入一个方法,其他实现不变
叶节点加入:
public Iterator createIterator() {
return new NullIterator();
}
public class MenuItem extends MenuComponent {
String name;
String description;
boolean vegetarian;
double price;
public MenuItem(String name,
String description,
boolean vegetarian,
double price)
{
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
public Iterator createIterator() {
return new NullIterator();
}
public void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
组合节点加入:
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
public class Menu extends MenuComponent {
ArrayList menuComponents = new ArrayList();
String name;
String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent =
(MenuComponent)iterator.next();
menuComponent.print();
}
}
}
先看看叶节点中加入的方法返回的迭代器:
public class NullIterator implements Iterator {
public Object next() {
return null;
}
public boolean hasNext() {
return false;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
很容易理解,由于叶节点不需要遍历,所以他的实现如上所示,重点是组合节点遍历的实现,我们直接上代码:
public class CompositeIterator implements Iterator {
Stack stack = new Stack();
public CompositeIterator(Iterator iterator) {
stack.push(iterator);
}
public Object next() {
if (hasNext()) {
Iterator iterator = (Iterator) stack.peek();
MenuComponent component = (MenuComponent) iterator.next();
if (component instanceof Menu) {
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}
public boolean hasNext() {
if (stack.empty()) {
return false;
} else {
Iterator iterator = (Iterator) stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
这段代码我不会做具体的解释,因为比较抽象,有一种只可意会不可言传的感觉,相信大家能看懂。