---
# System prepended metadata

title: 合成模式 Composite
tags: [Structural Patterns]

---

---
tags: Structural Patterns
---

# 合成模式 Composite
![](https://i.imgur.com/12x56i9.png)

白話文一點就是將所有的物件都視為同一種東西
讓物件可以是集合也可以是個體
ex:
![](https://i.imgur.com/6MsiPuj.png)
用這個圖做解釋

coach: 是輔導就業，提供工作上協助的人
engineer: 一般工程師
designer: 一般設計師

coach, engineer, designer都是由一個abstract class "employee" 延伸而成

component: employee
所有員工(coach, engineer, designer)的template
* 描述在 Composite 中物件的interface
* 實作一些共通的部分
* 定義存取、管理其 Child 物件的介面


composite: coach 
在被教的同時也能夠去教別人(能有child物件)
* 定義有 Child 物件的行為
* 儲存 Child 元件
* 實作 Child 相關的物件行為


leaf: engineer, designer
沒有child物件，實現單一功能

**架構能夠畫成樹狀圖**

> [name=Benson]


## 架構圖
![](https://i.imgur.com/doY7x3b.png)


## 角色


**Component: like the Glyph**
* 宣告複合體內含物件之介面。
* 對Leaf、Composite一視同仁

**Leaf**
* 複合結構之終端物件（不會有子節點）。
* 定義基本物件的行為。

**Composite: like the Row, Column**
* 定義含子結構的節點之行為。
* 儲存子節點。
* 實作出Componet之中與子結構有關的介面。

**Client 參與者**
* 利用 Composite Pattern 的人，例如 Main 類別。


## 構思
Leaf、Componsite繼承Component
呈現「部份(part) / 整體(whole)」的關係
represent part-whole
管理子節點的部分可以使用Iterator Pattern(新增、刪除)

## 安全性（Safety）和通透性（Transparency）的取捨
把增加或刪除等操作放在不同位置。
1. 若定義在Component：通透性較好，安全性較差。無法避免別人寫出「可增、刪Leaf子節點」的程式。**要設定丟出例外防止錯誤，Leaf還是可以使用add但是會丟出例外。**
3. 若定義在Composite：安全性較好，但通透性較差。因為Leaf和Composite的介面不同。**在add時需要轉成Composite的型態。**

![](https://i.imgur.com/zjPt0dI.png)

![](https://i.imgur.com/fDkDA8J.png)

## 菜單與菜單內容的範例

``` java

//Component
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();
	}
}

//componsite
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();
		}
	}
}
//leaf
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());
	}
}

public class Waitress {
	MenuComponent allMenus;

	public Waitress(MenuComponent allMenus) {
		this.allMenus = allMenus;
	}

	public void printMenu() {
		allMenus.print();
	}
}
//Client 
public class MenuTestDrive {
	public static void main(String args[]) {
		MenuComponent pancakeHouseMenu =
			new Menu("PANCAKE HOUSE MENU", "Breakfast");
		MenuComponent dinerMenu =
			new Menu("DINER MENU", "Lunch");
		MenuComponent cafeMenu =
			new Menu("CAFE MENU", "Dinner");
		MenuComponent dessertMenu =
			new Menu("DESSERT MENU", "Dessert of course!");
		MenuComponent coffeeMenu =
			new Menu("COFFEE MENU", "Stuff to go with your afternoon coffee");

		MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");

		allMenus.add(pancakeHouseMenu);
		allMenus.add(dinerMenu);
		allMenus.add(cafeMenu);

		pancakeHouseMenu.add(new MenuItem(
			"K&B's Pancake Breakfast",
			"Pancakes with scrambled eggs, and toast",
			true,
			2.99));
		pancakeHouseMenu.add(new MenuItem(
			"Regular Pancake Breakfast",
			"Pancakes with fried eggs, sausage",
			false,
			2.99));
		pancakeHouseMenu.add(new MenuItem(
			"Blueberry Pancakes",
			"Pancakes made with fresh blueberries, and blueberry syrup",
			true,
			3.49));
		pancakeHouseMenu.add(new MenuItem(
			"Waffles",
			"Waffles, with your choice of blueberries or strawberries",
			true,
			3.59));

		dinerMenu.add(new MenuItem(
			"Vegetarian BLT",
			"(Fakin') Bacon with lettuce & tomato on whole wheat",
			true,
			2.99));
		dinerMenu.add(new MenuItem(
			"BLT",
			"Bacon with lettuce & tomato on whole wheat",
			false,
			2.99));
		dinerMenu.add(new MenuItem(
			"Soup of the day",
			"A bowl of the soup of the day, with a side of potato salad",
			false,
			3.29));
		dinerMenu.add(new MenuItem(
			"Hotdog",
			"A hot dog, with saurkraut, relish, onions, topped with cheese",
			false,
			3.05));
		dinerMenu.add(new MenuItem(
			"Steamed Veggies and Brown Rice",
			"Steamed vegetables over brown rice",
			true,
			3.99));

		dinerMenu.add(new MenuItem(
			"Pasta",
			"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
			true,
			3.89));

		dinerMenu.add(dessertMenu);

		dessertMenu.add(new MenuItem(
			"Apple Pie",
			"Apple pie with a flakey crust, topped with vanilla icecream",
			true,
			1.59));

		dessertMenu.add(new MenuItem(
			"Cheesecake",
			"Creamy New York cheesecake, with a chocolate graham crust",
			true,
			1.99));
		dessertMenu.add(new MenuItem(
			"Sorbet",
			"A scoop of raspberry and a scoop of lime",
			true,
			1.89));

		cafeMenu.add(new MenuItem(
			"Veggie Burger and Air Fries",
			"Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
			true,
			3.99));
		cafeMenu.add(new MenuItem(
			"Soup of the day",
			"A cup of the soup of the day, with a side salad",
			false,
			3.69));
		cafeMenu.add(new MenuItem(
			"Burrito",
			"A large burrito, with whole pinto beans, salsa, guacamole",
			true,
			4.29));

		cafeMenu.add(coffeeMenu);

		coffeeMenu.add(new MenuItem(
			"Coffee Cake",
			"Crumbly cake topped with cinnamon and walnuts",
			true,
			1.59));
		coffeeMenu.add(new MenuItem(
			"Bagel",
			"Flavors include sesame, poppyseed, cinnamon raisin, pumpkin",
			false,
			0.69));
		coffeeMenu.add(new MenuItem(
			"Biscotti",
			"Three almond or hazelnut biscotti cookies",
			true,
			0.89));

		Waitress waitress = new Waitress(allMenus);

		waitress.printMenu();
	}
}
Java
Result:

```

>[name=閔致]
