原理或定義
組合模式又叫做部分-整體模式,它使我們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程序可以向處理簡單元素一樣來處理複雜元素,從而使得客戶程序與複雜元素的內部結構解藕。
結構
Component: 爲參加組合的對象聲明一個公共接口,
不管是組合還是葉結點.
Leaf: 在組合中表示葉子結點對象, 葉子結點沒有子結點.
Composite: 表示參加組合的有子對象的對象,
並給出樹枝購件的行爲;
類圖
案例與代碼
本模式採用的案例沿用上一篇文章,迭代器模式的餐廳合併的菜單問題項目。
需求變更:某一家店的菜單添加餐後甜點子菜單
餐廳菜單聚類結構現狀:
結構抽象
需要用樹形結構,節點是菜單或子菜單,葉子是菜單項
需要能夠在各個菜單項之間遊走,遍歷
要能夠有彈性的在菜單項之間遊走
組合模式的設計方案:
類圖:
迭代器組件類:
public class ComposeIterator implements Iterator {
private Stack<Iterator> stack = new Stack<Iterator>();
public ComposeIterator(Iterator iterator) {
stack.push(iterator);
}
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if (stack.empty()) {
return false;
}
Iterator iterator = stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub
if (hasNext()) {
Iterator iterator = stack.peek();
MenuComponent mMenuComponent = (MenuComponent) iterator.next();
stack.push(mMenuComponent.getIterator());
return mMenuComponent;
}
return null;
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}
public class NullIterator implements Iterator{
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return false;
}
@Override
public Object next() {
// TODO Auto-generated method stub
return null;
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}
菜單組件接口:
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(); } }
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() {
// TODO Auto-generated method stub
System.out.println(getName() + "***" + getPrice() + "***"
+ getDescription());
}
}
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() {
// TODO Auto-generated method stub
System.out.println("****This is SubMenu****");
};
// 其他功能代碼
}
餐廳類:
public class CakeHouseMenu extends MenuComponent {
private ArrayList<MenuComponent> menuItems;
public CakeHouseMenu() {
menuItems = new ArrayList<MenuComponent>();
addItem("KFC Cake Breakfast", "boiled eggs&toast&cabbage", true, 3.99f);
addItem("MDL Cake Breakfast", "fried eggs&toast", false, 3.59f);
addItem("Stawberry Cake", "fresh stawberry", true, 3.29f);
addItem("Regular Cake Breakfast", "toast&sausage", true, 2.59f);
}
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() {
// TODO Auto-generated method stub
System.out.println("****This is CakeHouseMenu****");
};
// 其他功能代碼
}
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() {
// TODO Auto-generated method stub
if (position < numberOfItems) {
return true;
}
return false;
}
@Override
public Object next() {
// TODO Auto-generated method stub
MenuComponent menuItem = menuItems[position];
position++;
return menuItem;
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println("****This is DinerMenu****");
};
}
服務員類:
public class Waitress {
private ArrayList<MenuComponent> iterators = new ArrayList<MenuComponent>();
public Waitress() {
}
public void addComponent(MenuComponent mMenuComponent) {
iterators.add(mMenuComponent);
}
public void printMenu() {
Iterator iterator;
MenuComponent menuItem;
for (int i = 0, len = iterators.size(); i < len; i++) {
iterators.get(i).print();
iterator = iterators.get(i).getIterator();
while (iterator.hasNext()) {
menuItem = (MenuComponent) iterator.next();
menuItem.print();
}
}
}
public void printVegetableMenu() {
Iterator iterator;
MenuComponent menuItem;
for (int i = 0, len = iterators.size(); i < len; i++) {
iterators.get(i).print();
iterator = iterators.get(i).getIterator();
while (iterator.hasNext()) {
menuItem = (MenuComponent) iterator.next();
if (menuItem.isVegetable()) {
menuItem.print();
}
}
}
}
}
測試類:
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();;
}
}
這個重點在於迭代器組件類,這個類對菜單項和子菜單項中的菜單項進行了遞歸邏輯處理。
組合模式能讓客戶以一致的方式來處理個別對象以及對象組合。
也就是我們可以忽略對象組合與個體對象之間的差別
使用場景
1. 表示整體-部分的層次結構。
2.希望用戶忽略組合對象與單個對象的不同,用戶將統一的使用組合結構中的所有對象
優缺點
主要優點有:
1. 定義了包含基本對象和組合對象的類的層次結構。基本對象可以被組合成更復雜的組合對象,這個組合又會被組合,不斷遞歸下去。
2.簡化客戶代碼。客戶可以一致的使用組合結構和單個對象。
3.使得更容易增加新增類型的組件。
缺點主要有:
很難限制組合中的組件。有時希望一個組合只能有某些特定組件。使用Composite時,不能依賴類型系統施加這些約束,而必須要在運行時刻進行檢查。