由於自己的工作中不曾使用過類似Builder Pattern的情境,
因此剛讀完並沒有特別深刻的體會與連結,
但我盡量地去找出能應用的場景應該是什麼,來加強這個Pattern的學習記憶。
Builder Pattern 建造者模式
我自己認為大話設計模式中Builder的第一個範例還不足以完全滿足建造者模式,
因為這個範例沒有明確的Product類別 (但也沒有一定的對或錯),
所以直接練習他第二個完整建造者模式的範例,請參考 Chapter_13.1,
先來看類別圖:
說明一下個個類別負責的功能。
Prodduct:定義建立後要使用的產品物件應有什麼功能。
ConcreteBuilder:定義建立產品物件時各個功能的參數細節。
Builder:規範所有的ConcreteBuilder應該要去執行的那些"建立參數細節"function。
Director:定義ConcreteBuilder內的建立function的執行順序。
看到這邊,我想應該還是一頭霧水,再繼續來看細節部分。
來看Product內定義了什麼,
作者這個範例很簡單,只定義一個Add()來作為Product的功能,
[所以我覺得這個範例並不是那麼直覺,很難聯想到實際應用情形。]
接著Builder定義出所有的ConcreteBuilder應該要實做的項目,
他們都必須實做PartA與PartB用以建立Product的Add() function,
而PartA與PartB的差異僅僅是參數的不同,細節定義在ConcreteBuilder中。
兩個ConcreteBuilder實作的細節如下,而這也是這個Pattern最重要的部分之一,
使用者可以定義多種不同的參數,隨時替換參數以建立出不同參數打造的Product物件。
可以觀察到ConcreteBuilderOne實作參數為hand與arm,
ConcreteBuilderTwo實作參數為leg與foot。
完成這些之後,來看這個Pattern另一個最重要的部分-Director,
Director的實作定義了這些零組件的"建立順序",先建立PartB再組裝PartA:
到目前為止我們已經擁有了Builder Pattern中最重要的兩個元素,
"建立物件的參數"與"建立物件的順序"。
來看看使用者使用上會有什麼效果吧。
由於我們已經在ConcreteBuilder定義好建立Product需要的參數了,
因此現在要透過Director去組裝,而組裝步驟也定義在Construct()中,
所以當使用者需要使用第一套參數(ConcreteBuilderOne)所建立的Product物件,依照以下步驟:
步驟一:
先把參數設定檔ConcreteBuilder建立出來,
步驟二:
再把這個參數設定檔交給Director去組裝成Product物件。
要切換成第二套參數也非常簡單,
依照上述兩個步驟就可以建立出完全不同屬性的Product物件了。
但我覺得這個例子很難連結到實際的應用情境。
實際(?)應用情境
如果套上我自己想像的情境,來看 Chapter_13.2 。我們把情境設定為,
Product是自動化機台的光學量測模組(Automated Optical Inspection , AOI),
而這個量測模組內有三個重要組件需要初始化建立,
分別是OpticalSensor 光學探頭、Stage 移動平台、Robot 機械手臂,
並假設我們使用的廠牌條列如下:
- OpticalSensor
- OpticalBrand A
- OpticalBrand B
- Stage
- StageBrand O
- StageBrand P
- Robot
- RobotBrand Y
- RobotBrand Z
因此Product類別就可以定義成以下類別:
而經過測試,硬體的啟動順序必須為 Robot -> OpticalSensor -> Stage,
否則就會錯誤,
那我們就可以定義Director類別如下:
現在假設客戶Martin的機台硬體配置需求為:
OpticalSensor 選用 OpticalBrand A
Stage 選用 StageBrand O
Robot 選用 RobotBrand Y
而客戶Leo的機台硬體配置需求為:
OpticalSensor 選用 OpticalBrand B
Stage 選用 StageBrand P
Robot 選用 RobotBrand Z
那麼就可以為這兩位客戶各自建立一套Config參數,用以建立AOI產品,
Martin客戶的參數命名為ConfigOne,Leo客戶的命名參數為ConfigTwo,
ConfigOne類別定義如下:
ConfigTwo類別定義如下:
目前為止,我們已經準備好Builder Pattern最重要的兩個元素,
負責建立步驟的Director 與 目標物件的參數設定AOI_ConfigBuilder,
因此我們就可以透過Director來指揮AOI_ConfigBuilder建立出我們需要的AOI物件,
來看看使用者如何呼叫:
如果仔細比較一下,可以發現如果已經有開發好的架構,
想要為不同的客戶替換不同廠牌的零件來使用這個架構時,
只需要幫這名客戶編寫一個ConfigBuilder類別,
再更換橘框中的參數類別即可,就可以穩定的產生一個AOI物件來使用,
而這邊又可以寫成 Strategy Pattern ,
讓使用者在Config檔案內設定好之後(例如開放一個.ini檔讓客戶修改),
啟動程式時就可以讀取Config檔內的參數,自動選擇要使用ConfigOne還是ConfigTwo,
而程式內就可以達成不必修改程式碼就執行想要的版本。
實作成本
不過寫成Builder Pattern的實作成本也滿高的,
看看檔案目錄內的AOI Builder Pattern資料夾與類別圖:
目前只舉裡三種硬體設備,每個硬體設備只有兩種廠牌,也只有兩個ConcreteBuilder,
看起來就有點亂了,如果再龐大一點,不知道會長成什麼樣子,
所以使用Pattern也是需要付出代價的,所以才會有軟體架構師來專門負責這樣子的規劃吧。
與其他 Factroy Pattern 有什麼差異?
建造者是為了建立物件,那他和工廠相關Pattern有什麼不同?
我的想法是,工廠是為了集中"new"的地方,而建造者是關注"如何new",
"如何new"包含了new的順序與參數。
但我覺得建造者反而更像 Template Method 範本方法,都是規範子類如何進行操作,
但他們之間的差異在哪呢?
其實如果把範本方法類別中的那個範本方法取出來是不是就很像Director的功能?
儘管兩者都是在規範子類進行一系列受規範的操作,
但範本方法目的就僅止於規範操作步驟,
而製造者則是規範操作並以特定參數去"生成"一個物件,最終目的是生成物件,
因此兩個的目的是不同的。
沒有留言:
張貼留言