目的
在子類別上決定要如何具現類別的物件。
模式
建立型模式 (Creational Pattern)
時機
在多個相似的類別中,依照需求建立出同一介面的物件。(創建步驟可能不同,也有可能是參數不同…)
步驟
將建立物件的方法 (factory method) 抽出至父類別或是介面中,這些相似的子類別,只需要繼承父類別,並覆寫建立物件的方法。
範例
有兩個類別 HDCameraRecorderCreator 以及 CameraRecorderCreator,分別建立高畫質的 CameraRecorder (HDCameraRecorder) 以及普通畫質的 CameraRecorder (CameraRecorder)。
其中 CameraRecorder 以及 HDCameraRecorder 都是實作 ICameraRecorder
的物件。
我們注意到, ICameraRecorder
是設定為 lateinit
(延遲初始化) 且在 record()
中才被實例化。
操作步驟
- 抽取方法 (Extract Method)
- 將實例化
ICameraRecorder
的部分抽成一個名為createCameraRecorder
的方法,也就是 factory method (工廠方法)。 - 其中,
createCameraRecorder
的回傳型別為ICameraRecorder
2. 抽出父類別 (Extract Superclass)
將 CameraRecorderCreator
及 HDCameraRecorderCreator
中的 factory method: createCameraRecorder() 抽出至父類別 AbstractCameraRecorderCreator
3. 繼承父類別
- 將
CameraRecorderCreator
及HDCameraRecorderCreator
繼承AbstractCameraRecorderCreator
- 在
CameraRecorderCreator
及HDCameraRecorderCreator
中的createCameraRecorder
就要加上override
修飾符
4. 將 lateinit var cameraRecorde: ICameraRecorder
也搬到父類別中。
5. 我們發現, record()
也可以搬到父類別中,因為在兩個類別中所做的動作都相同。
- 將
record()
搬到父類別後,cameraRecorder
的訪問修飾符 (Access Modifier) 就能改成private
。
6. 完成
在子類別呼叫 createCameraRecorder()
來決定要建立哪一個類別。
優點與缺點
+子類別利用 factory method 可以依照類別的需求建立不同的物件 (相同介面)。
+在父類別中,把可重複利用的項目保留在父類別中,如 record()
,如此就可以減少子類別重複的內容。
— 如果建立物件需要不同的參數,在 factory method 就會包含所有的參數,縱使子類別不需要。
心得
factory method 是最容易的 Design Patterns,雖然容易,但也很重要。
為了要使 factory method 能應用在不同的類別,我們必須要將物件共同的內容抽出成介面。在不同的類別呼叫時,我們就可以回傳其介面而不是實作。
Program to an interface, not an implementation
Design Patterns - GoF
以前都是直接回傳其實作,用 factory method 之後,就可以限制我們回傳實作這個動作。
回傳介面的好處,對於程式未來的擴展性是更高的,因為如果有類似的類別,我只要實作該介面,並且令某一子類別實作該類別即可。