Composite Pattern
===
###### tags: `Design Pattern` `Composite Pattern`
# 小故事
1. 菜單/選單
老王和阿花上次去StarBuck讀書約會之後,
這周想去電影院看神力女超人1984, 電影院的飲料是1杯50$, 口味自己選擇.

門口擺著一台汽水調配器, 如上圖. 這台可以提供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```, 根據性別、年紀不同來發放不同類型的活動序號, 刺激目標客群的消費來達到此目的.

```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.



4. AST語法樹
```sql=
SELECT column0 FROM table0 UNION SELECT column1 FROM table1 WHERE a = 1;
```
這段經過Parser會被解析成一個與SQL語句

左子樹和右子樹, 本身就是個完整的樹. 只是被組合進來.
# 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有點像, 都是在類別當中儲存成員變數來完成功能上的擴展, 那有何不同呢?