---
# System prepended metadata

title: Composite Pattern
tags: [Composite Pattern, Design Pattern]

---

Composite Pattern
===
###### tags: `Design Pattern` `Composite Pattern`

# 小故事
1. 菜單/選單
老王和阿花上次去StarBuck讀書約會之後, 
這周想去電影院看神力女超人1984, 電影院的飲料是1杯50$, 口味自己選擇.
![](https://i.imgur.com/nDHOcSn.jpg)
門口擺著一台汽水調配器, 如上圖. 這台可以提供6種品牌的飲料(可苦可樂、芬達、雪碧、百事、維大力?、麥根)跟蘇打水, 選好品牌後, 在選擇口味; 可苦可樂有提供可樂, Zero, 香草, 櫻桃, 纖維+...; 芬達有橘子、蘋果、葡萄、青檸、芒果、水蜜桃、西瓜...

思考了一下, 這裡的飲品提供的選擇, 是一種```階層結構```
Soda本身是一個Root Component; 品牌則是Child Component;汽水口味則是Leaf Component.

```plantuml
@startuml
Sodas ^-- CocaCola
Sodas ^-- Fanta
Sodas ^-- Sprite
Sodas ^-- Pepsi
Sodas ^-- Vitali
Sodas ^-- RootBeer
Sodas ^-- Soda

CocaCola ^-- Cola
CocaCola ^-- ColaZero
CocaCola ^-- ColaVanilla
CocaCola ^-- ColaCherry

Fanta ^-- Orange
Fanta ^-- Apple
Fanta ^-- Grape

RootBeer ^-- Strawberry
RootBeer ^-- Vanilla
@enduml
```
簡化了一下大概這樣呈現. 然後就建模存入資料庫了.
一切看似美好, 想說這樣設計
```sql=
CREATE TABLE `brand` (
  `brand` int(11) unsigned NOT NULL,
  `name` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`brand`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `sodas` (
  `id` int(10) unsigned NOT NULL,
  `name` varchar(45) NOT NULL,
  `brand_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
```
Sodas每個產品都有對到某一個品牌.
疑? 品項中, 有一個Soda蘇打水的產品沒品牌阿QQ
沒差, 建個自有品牌Self, Soda放在它底下.

```plantuml
@startuml
Sodas ^-- CocaCola
Sodas ^-- Fanta
Sodas ^-- Sprite
Sodas ^-- Pepsi
Sodas ^-- Vitali
Sodas ^-- RootBeer
Sodas ^-- Self

CocaCola ^-- Cola
CocaCola ^-- ColaZero
CocaCola ^-- ColaVanilla
CocaCola ^-- ColaCherry

Fanta ^-- Orange
Fanta ^-- Apple
Fanta ^-- Grape

RootBeer ^-- Strawberry
RootBeer ^-- Vanilla

Self ^-- Soda
@enduml
```

----------
2. 決策樹
今天週一, 企劃想對遊戲內的玩家做個活動, 來發放活動序號, 兌換SSR武將.
下班前就要, 男性玩家發放SSR貂蟬, 女性玩家發放SSR關羽(草,男女不平等阿)
想了想, 不難, 就如下去修改

```go=
for player := range players {
    if player.Gender == Male {
        player.SendNotify("SSR貂蟬序號:XXYY1234")
    } else {
        player.SendNotify("SSR關羽序號:XXYY1235")
    }
}
```
好! 打完收工, 開心下班領SSR魔關羽(?)


週二來上班了, 企劃表示, 昨天活動反應非常好, 下載次數激增!!!
今天我們要推出新活動, 按照年紀, 年輕、成年、中年, 不同年紀做判斷, 要對不同年齡層做不同的刺激消費:
年輕人(<18)就贈送S級武將*5.
成年人([18, 25])送S級武將*10.
中年人([25,?])送SS級武將*5.
很急, 今天就要!!! 做完, 這個月業績獎金+5000$

```go=
for player := range players {
    switch {
    case player.Age < 18:
        player.SendNotify("S級武將*5")
    case player.Age < 25:
        player.SendNotify("S級武將*10")
    case player.Age < 999 :
        player.SendNotify("SS級武將*5")
    }
}
```
很棒! 打完收工, 開心下班領S級武將*5(?)

週三說要, 區分單身、結婚、有小孩的加上判斷
週四說要針對學生身份且非單身的做特別禮包...

... 這團代碼被一堆if-else/switch給弄的沒人看得懂了.
且還很難測試,被客戶投訴都沒收到序號QQ

週末來加班, 隔天通知業績獎金-5000$

這是非常簡化的行銷規則```決策樹DecisionTree```, 根據性別、年紀不同來發放不同類型的活動序號, 刺激目標客群的消費來達到此目的.

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

```go=
func Process(player model.Player) {
	if player.Gender == model.FEMALE {
		switch {
		case player.Age < 18:
			log.Println("S級武將*10")
		case player.Age < 25:
			log.Println("S級武將*15")
		case player.Age < 255:
			log.Println("SS級武將*10")
		}
	} else {
		switch {
		case player.Age < 18:
			log.Println("S級武將*5")
		case player.Age < 25:
			log.Println("S級武將*10")
		case player.Age < 255:
			log.Println("SS級武將*5")
		}
	}
	return
}
```
為了求快, 波動拳開始了- .-
[參考網站](https://www.mdeditor.tw/pl/pBGR/zh-tw)

----------
3. 資料庫一個表中, 該row有的parent_id的欄位指向同一表中其他的row record.
![](https://i.imgur.com/P4VZbAt.png)
![](https://i.imgur.com/R1MMEpX.png)

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


4. AST語法樹
```sql=
SELECT column0 FROM table0 UNION SELECT column1 FROM table1 WHERE a = 1;
```
這段經過Parser會被解析成一個與SQL語句
![](https://i.imgur.com/DL1YYA5.png)
左子樹和右子樹, 本身就是個完整的樹. 只是被組合進來.


# Composite Pattern
> In software engineering, the composite pattern is a **partitioning** design pattern. 
> The composite pattern describes a group of objects that are treated the same way as a single instance of the same type of object. 
> The intent of a composite is to "compose" objects into **tree structures** to represent **part-whole** hierarchies. 
> Implementing the composite pattern lets clients **treat** individual objects and compositions **uniformly**.

[UML](https://refactoringguru.cn/design-patterns/composite)

# 小問題
* 既然要client針對Leaf和Compitee是一樣的, 那怎麼Leaf裡面沒有add/remove/getChildren等行為呢?
* 跟Decorator有點像, 都是在類別當中儲存成員變數來完成功能上的擴展, 那有何不同呢?


