一、组合模式简介
Composite模式也叫组合模式,是构造型的设计模式之一。通过递归手段来构造树形的对象结构,并可以通过一个对象来访问整个对象树。
二、组合模式的结构
三、组合模式的角色与职责
- Component (树形结构的节点抽象)
为所有的对象定义统一的接口(公共属性,行为等的定义)
提供管理子节点对象的接口方法
[可选]提供管理父节点对象的接口方法 - Leaf (树形结构的叶节点) :Component的实现子类
- Composite(树形结构的枝节点) :Component的实现子类
四、组合模式的具体实现
我们在前一个迭代器模式中的三个餐厅合并了,但是并没有重新设计新的菜单,服务员一直抱怨,每次都要记住那么多的接口很麻烦,因此我们开始探索一种能够让服务员只需要一个接口就能遍历所有菜单的形式。
那么我们需要什么呢?
- 我们需要某种树形结构,可以容纳菜单、子菜单和菜单项
- 我们需要在每个菜单之间的各个项之间游走
使用组合模式
组合模式能让我们以属性的方式创建对象的结构,书里面包含了组合即菜单以及个别的对象即菜单项。使用组合我们能把相应的操作应用在组合和个别对象上,换句话说我们可以忽略组合和个别对象之间的差异。
方案设计
类设计
由于我们需要无差别的处理菜单和菜单项,所以我们需要定有一个共同接口菜单组建,菜单和菜单项都需要实现该接口一边能一致的处理。
由于菜单和菜单项是不同的实现,所以菜单组建表现得是两个角色。包含了所有的方法并提供默认的操作,而菜单和菜单项在实现是只覆盖所需要的操作即可。下面我们看一下菜单组件的设计:
// An highlighted block
package design.composite.gys.composite;
public abstract class MenuComponent {
public String getName() {
throw new UnsupportedOperationException();
}
public String getDesc() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isV() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
public void add(MenuComponent e) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent e) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
}
接下来我们设计菜单的类,我们覆盖菜单所独有的方法,剩余的均使用默认操作抛出异常:
// An highlighted block
package design.composite.gys.composite;
import java.util.ArrayList;
import java.util.Iterator;
public class Menu extends MenuComponent{
ArrayList menuComponents=new ArrayList();
private String name;
private String desc;
public Menu(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
public void print() {
System.out.print(getName()+" ");
System.out.println("--"+getDesc());
System.out.println("-------------");
Iterator it=menuComponents.iterator();
while(it.hasNext())
{MenuComponent menuComponent=(MenuComponent)it.next();
menuComponent.print();
}
System.out.println("****************");
}
public void add(MenuComponent e) {
menuComponents.add(e);
}
public void remove(MenuComponent e) {
menuComponents.remove(e);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);}
}
同样的对于菜单项:
// An highlighted block
package design.composite.gys.composite;
public class MenuItem extends MenuComponent{
private String name;
private String desc;
private double price;
private boolean isv;
public MenuItem(String name, String desc , boolean isv,double price) {
super();
this.name = name;
this.desc = desc;
this.price = price;
this.isv = isv;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
public double getPrice() {
return price;
}
public boolean isV() {
return isv;
}
public void print() {
System.out.print(getName()+" ");
if(isV())
System.out.println("(V) ");
System.out.print(getPrice()+" ");
System.out.println("--"+getDesc());
}
}
接下来我们需要重新定义我们的服务员了:
// An highlighted block
package design.composite.gys.composite;
public class Waitress {
MenuComponent Allmenu;
public Waitress(MenuComponent allmenu) {
super();
Allmenu = allmenu;
}
public void printMenu() {
Allmenu.print();
}
}
可以看到,现在的服务员只需要提供总的菜单入口,便能在菜单中完全实现输出。
实现我们的菜单并测试服务员:
// An highlighted block
package design.composite.gys.composite;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
MenuComponent pancakeHouseMenu=new Menu("Pancanke house menu","Breakfast");
MenuComponent dinnerMenu=new Menu("Dinner menu","Lunch");
MenuComponent cafeMenu=new Menu("Cafe menu","dinner");
MenuComponent dessertMenu=new Menu("Dessert menu","Dessert");
pancakeHouseMenu.add(new MenuItem("a1","b1",true,1.99));
pancakeHouseMenu.add(new MenuItem("c1","d1",false,2.99));
pancakeHouseMenu.add(new MenuItem("e1","f1",true,3.99));
pancakeHouseMenu.add(new MenuItem("g1","h1",false,4.99));
dinnerMenu.add(new MenuItem("a2","b2",true,6.99));
dinnerMenu.add(new MenuItem("c2","d2",false,7.99));
dinnerMenu.add(new MenuItem("e2","f2",true,8.99));
dinnerMenu.add(new MenuItem("g2","h2",false,9.99));
cafeMenu.add(new MenuItem("a3","b3",true,6.99));
cafeMenu.add(new MenuItem("c3","d3",false,7.99));
cafeMenu.add(new MenuItem("e3","f3",true,8.99));
cafeMenu.add(new MenuItem("g3","h3",false,9.99));
dessertMenu.add(new MenuItem("a4","b4",true,6.99));
dessertMenu.add(new MenuItem("c4","d4",false,7.99));
dessertMenu.add(new MenuItem("e4","f4",true,8.99));
dessertMenu.add(new MenuItem("g4","h4",false,9.99));
dinnerMenu.add(dessertMenu);
MenuComponent allmenu=new Menu("All menu","All menu combined!");
allmenu.add(pancakeHouseMenu);
allmenu.add(dinnerMenu);
allmenu.add(cafeMenu);
Waitress w=new Waitress(allmenu);
w.printMenu();
}
}
让我们看一下服务员能不能输出所有的菜单:
// An highlighted block
-------------
All menu --All menu combined!
-------------
-------------
Pancanke house menu --Breakfast
-------------
a1 (V)
1.99 --b1
c1 2.99 --d1
e1 (V)
3.99 --f1
g1 4.99 --h1
****************
-------------
Dinner menu --Lunch
-------------
a2 (V)
6.99 --b2
c2 7.99 --d2
e2 (V)
8.99 --f2
g2 9.99 --h2
-------------
Dessert menu --Dessert
-------------
a4 (V)
6.99 --b4
c4 7.99 --d4
e4 (V)
8.99 --f4
g4 9.99 --h4
****************
****************
-------------
Cafe menu --dinner
-------------
a3 (V)
6.99 --b3
c3 7.99 --d3
e3 (V)
8.99 --f3
g3 9.99 --h3
****************
****************
轻松加愉快的就完成了。