一、组合模式
将对象聚合成树形结构来表现“整体/部分”的层次结构
组合模式
二、示例
本次示例延用上一个文章《迭代器模式》的菜单整合项目:
本次的需求是将在中餐厅的菜单中添加一块甜点菜单组,遍历输出菜单时也要遍历新增的甜点菜单
则满足本次需求需要用到组合模式,将项目对象设计成类似数据结构中树的对象,菜单项相当于是叶子节点,叶子节点下仍可拓展新的子类叶子节点
- 需要用树形结构,节点是菜单或子菜单,叶子是菜单项
- 需要能够在各个菜单项之间游走,遍历
- 要能够有弹性的在菜单项之间游走
1.定义一个统一属性的父类节点对象
/**
* 组合对象的超类,被所有子类继承,相当于统一的节点属性
*/
public abstract class MenuComponent {
public String getName() {
return "";
}
public String getDescription() {
return "";
}
public float getPrice() {
return 0;
}
public boolean isVegetable() {
return false;
}
public abstract void print();
public Iterator getIterator() {
return new NullIterator();
}
}
2.定义统一的子类菜单对象
/**
* 组合模式,子菜单对象,继承超类组合对象
*/
public class MenuItem extends MenuComponent {
private String name, description;
private boolean vegetable;
private float price;
public MenuItem(String name, String description, boolean vegetable, float price) {
this.name = name;
this.description = description;
this.vegetable = vegetable;
this.price = price;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public float getPrice() {
return price;
}
@Override
public boolean isVegetable() {
return vegetable;
}
@Override
public void print() {
System.out.println(getName() + "***" + getPrice() + "***"
+ getDescription());
}
}
3.组合对象引申出来的组合迭代器,利用栈存放子类的迭代器
/**
* 组合模式,组合对象的迭代器,利用栈存放子类的迭代器
*/
public class ComposeIterator implements Iterator {
private Stack<Iterator> stack = new Stack<Iterator>();
public ComposeIterator(Iterator iterator) {
stack.push(iterator);
}
// 一个迭代器一个迭代器的遍历,利用递归的方式
@Override
public boolean hasNext() {
if (stack.empty()) {
return false;
}
Iterator iterator = stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
@Override
public Object next() {
if (hasNext()) {
Iterator iterator = stack.peek();
MenuComponent mMenuComponent = (MenuComponent) iterator.next();
stack.push(mMenuComponent.getIterator());
return mMenuComponent;
}
return null;
}
@Override
public void remove() {
}
}
4.定义新增的甜点菜单,也继承统一的父类菜单对象
/**
* 组合模式,新增的甜品菜单对象,中餐厅菜单下的子类菜单
*/
public class SubMenu extends MenuComponent {
private ArrayList<MenuComponent> menuItems;
public SubMenu() {
menuItems = new ArrayList<MenuComponent>();
addItem("Apple Cookie", "Apple&candy&Cookie", true, 1.99f);
addItem("Banana Cookie", "Banana&candy&Cookie", false, 1.59f);
addItem("Orange Cookie", "Orange&Cookie", true, 1.29f);
}
private void addItem(String name, String description, boolean vegetable,
float price) {
MenuItem menuItem = new MenuItem(name, description, vegetable, price);
menuItems.add(menuItem);
}
public Iterator getIterator() {
return new ComposeIterator(menuItems.iterator());
}
@Override
public void print() {
System.out.println("****This is SubMenu****");
}
// 其他功能代码
}
5.在中餐厅菜单中添加上新增的子菜单即可
/**
* 组合模式,中餐菜单对象,继承超类的组合对象,统一所有对象的属性
*/
public class DinerMenu extends MenuComponent {
private final static int Max_Items = 5;
private int numberOfItems = 0;
private MenuComponent[] menuItems;
public DinerMenu() {
menuItems = new MenuComponent[Max_Items];
addItem("vegetable Blt", "bacon&lettuce&tomato&cabbage", true, 3.58f);
addItem("Blt", "bacon&lettuce&tomato", false, 3.00f);
addItem("bean soup", "bean&potato salad", true, 3.28f);
addItem("hotdog", "onions&cheese&bread", false, 3.05f);
addSubMenu(new SubMenu());
}
private void addItem(String name, String description, boolean vegetable,
float price) {
MenuItem menuItem = new MenuItem(name, description, vegetable, price);
if (numberOfItems >= Max_Items) {
System.err.println("sorry,menu is full!can not add another item");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems++;
}
}
private void addSubMenu(MenuComponent mMenuComponent) {
if (numberOfItems >= Max_Items) {
System.err.println("sorry,menu is full!can not add another item");
} else {
menuItems[numberOfItems] = mMenuComponent;
numberOfItems++;
}
}
public Iterator getIterator() {
return new ComposeIterator(new DinerIterator());
}
class DinerIterator implements Iterator {
private int position;
public DinerIterator() {
position = 0;
}
@Override
public boolean hasNext() {
if (position < numberOfItems) {
return true;
}
return false;
}
@Override
public Object next() {
MenuComponent menuItem = menuItems[position];
position++;
return menuItem;
}
@Override
public void remove() {
}
}
@Override
public void print() {
System.out.println("****This is DinerMenu****");
}
}
6.由于所有菜单都继承自统一的父类菜单对象,迭代器也使用组合迭代器,则菜单项可一直深层次的添加子类单
/**
* 组合模式,类似于数据结构中的树形结构
* 所有对象为一个统一的节点属性,则需要定一个超类对象,子类均继承此父类,则可循环迭代调用
*/
public class MainTest {
public static void main(String[] args) {
Waitress mWaitress = new Waitress();
CakeHouseMenu mCakeHouseMenu = new CakeHouseMenu();
DinerMenu mDinerMenu = new DinerMenu();
mWaitress.addComponent(mCakeHouseMenu);
mWaitress.addComponent(mDinerMenu);
mWaitress.printVegetableMenu();
}
}
三、总结
- 组合模式能让客户以一致的方式来处理个别对象以及对象组合。 也就是我们可以忽略对象组合与个体对象之间的差别
- 将对象组合成树形结构以表示“部分-整体”的层次结构。
- 组合模式使得用户对单个对象和组合对象的使用具有唯一性
Java设计模式所有示例代码,持续更新中