组合设计模式让我们能用树形方式创建对象的结构,树里面包含了组合及个别的对象。是用组合结构,我们能把相同的操作应用在组合和个别对象上。话句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
---------援引自《Head First》
接着前面所写的迭代器模式,现在需要给午餐加一个子菜单。此时结构如下图所示:
(如果是我来做,我可能会在MenuItem里面再加个list 或者 array ,但仅仅是构思一下就觉得很头痛。)
不能把子菜单赋值给午餐菜单的item。因为午餐菜单的item的类型是MenuItem,子菜单的类型可能是list或者数组或者map,但不是MenuItem。现在代码需要重新构思,我们需要的设计是能够实现:
1.需要树形结构,可以容纳菜单、子菜单、菜单项。
2.我们需要能够在每个菜单的各个项之间游走。
3.我们也需要能够弹性的再菜单项之间游走。
我们的菜单结构就像这下面树形结构的草图:
关于上面第一条无须解释,关于第二条,意思就是我们能够遍历所有的菜单项,就像上图左下角的图所示。关于第三条,意思是说,我们可以灵活的选择去遍历我们想要遍历的那个菜单,比如说只去遍历图中众多菜单中的DinnerMenu,就像上图右下角所所示。
关于树形结构,具有子元素的节点叫做“节点”,没有子元素的节点叫做“叶节点”,我们可以把一个大树视为一个整体来操作,也可以把树中的“小树”当做一个小的整体来操作。(就像你可以命令一个团做一件事,也可以从这个团中抽出一个连,命令这个连做同样的事)。
关于组合模式类图:
抽象出来是这样的。leaf就是叶节点,对应的就是MenuItem这个小虾米,composite就是节点,对应的是菜单,这里composite定义组合节点的行为,leaf定义了组合内元素的行为,leaf 和 composite 都实现了接口中的一些方法,但对于各自来说有些方法是没有意义的,毕竟内部结构不同。针对我们要做的需求来说,与上图结构相同,只是接口中方法需要重新构思。接口里需要有getName(),getPrice(),getChild(int),isVegetarian()等方法,对于菜单来说isVegetarian()是没有意义的(你无法说一个菜单是素的还是荤的,因为这是菜的属性)。所以说leaf 和 composite不需要实现接口里全部的方法,而且我们希望这个接口可以提供这些方法的默认实现,于是我们可以把这个接口定义为抽象类,书里管这个类叫做“菜单组件”。
public abstract class MenuComponent {
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public void print(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public MenuComponent getChild(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public String getName(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public String getDescription(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public boolean isVegetrarian(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
public double getPrice(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
}
我们的菜单项和菜单,即“叶节点”和“”节点需要继承MenuComponent来实现组合内元素的行为和组合的行为。
package com.buxiaohui.menu;
import android.util.Log;
import java.util.ArrayList;
public class MenuItem extends MenuComponent {
private ArrayList menuComponent = new ArrayList();
private String name;
private String description;
private boolean vegetarian;
private 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 void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isVegetarian() {
return vegetarian;
}
public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public void print() {
Log.d("debug", "name = " + getName());
if (isVegetarian()) {
Log.d("debug", "(V)");
}
Log.d("debug", "price=" + getPrice());
Log.d("debug", "desc=" + getDescription());
Log.d("debug","===============");
}
}
package com.buxiaohui.menu;
import android.util.Log;
import java.util.ArrayList;
import java.util.Iterator;
public class CommonMenu extends MenuComponent {
ArrayList menuComponents = new ArrayList();
String name;
String description;
public CommonMenu(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 void print() {
Log.d("debug", "name = " + getName());
Log.d("debug","desc=" + getDescription());
Log.d("debug","=====================================");
Iterator iterator = menuComponents.iterator();
while(iterator.hasNext()){
MenuComponent menuComponent = (MenuComponent)iterator.next();
menuComponent.print();
}
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
====MainActivity====
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MenuComponent menuComponent0 = new CommonMenu("早餐-菜单","我是早餐菜单");
MenuComponent menuComponent1 = new CommonMenu("午餐-菜单","我是午餐菜单");
MenuComponent menuComponent2 = new CommonMenu("甜点-菜单","我是甜点菜单");
MenuComponent allMenus = new CommonMenu("All Menus","All Menus!");
allMenus.add(menuComponent0);
allMenus.add(menuComponent1);
allMenus.add(menuComponent2);
menuComponent0.add(new com.buxiaohui.menu.MenuItem("早餐0","test0-name0000000000",true,100.00));
menuComponent0.add(new com.buxiaohui.menu.MenuItem("早餐1","test0-name1111111111",true,100.01));
menuComponent2.add(new com.buxiaohui.menu.MenuItem("甜点0","甜点0甜点0甜点0",true,100.00));
menuComponent2.add(new com.buxiaohui.menu.MenuItem("甜点1","甜点1甜点1甜点1",true,101.01));
menuComponent2.add(new com.buxiaohui.menu.MenuItem("甜点2","甜点2甜点2甜点2",true,102.02));
menuComponent2.add(new com.buxiaohui.menu.MenuItem("甜点3","甜点3甜点3甜点3",true,103.03));
menuComponent0.add(menuComponent2);
menuComponent1.add(menuComponent2);
Waitress waitress = new Waitress(allMenus);
waitress.print();
}
}
=======运行结果=====
运行结果显示,早餐菜单除了若干个菜单项(MenuItem),还有子菜单也打印出来了。
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = All Menus
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=All Menus!
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ =====================================
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 早餐-菜单
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=我是早餐菜单
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ =====================================
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 早餐0
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=100.0
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=test0-name0000000000
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 早餐1
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=100.01
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=test0-name1111111111
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点-菜单
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=我是甜点菜单
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ =====================================
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点0
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=100.0
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点0甜点0甜点0
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点1
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=101.01
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点1甜点1甜点1
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点2
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=102.02
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点2甜点2甜点2
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点3
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.470 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=103.03
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点3甜点3甜点3
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 午餐-菜单
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=我是午餐菜单
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ =====================================
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点-菜单
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=我是甜点菜单
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ =====================================
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点0
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=100.0
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点0甜点0甜点0
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点1
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=101.01
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点1甜点1甜点1
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点2
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=102.02
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点2甜点2甜点2
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点3
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=103.03
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点3甜点3甜点3
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点-菜单
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=我是甜点菜单
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ =====================================
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点0
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=100.0
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点0甜点0甜点0
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点1
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=101.01
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点1甜点1甜点1
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点2
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=102.02
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点2甜点2甜点2
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ name = 甜点3
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ (V)
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ price=103.03
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ desc=甜点3甜点3甜点3
05-25 13:57:45.471 20011-20011/com.buxiaohui.myapplication D/debug﹕ ===============
05-25 13:57:45.472 20011-20011/com.