Simple Factory Pattern,把複雜細節隱藏的小工廠
Simple Factory Pattern,把複雜細節隱藏的小工廠

Simple Factory Pattern,把複雜細節隱藏的小工廠

Tags
Golang
Hey! Go Design Patterns
ithome 2021 ironman
Date
Sep 14, 2021
工廠模式主要有三種不同的實作:
  • Simple Factory Pattern
  • Factory Method Pattern
  • Abstract Factory Pattern
這三種實作由簡單到複雜,今天會介紹最單純的 Simple Factory Pattern

什麼是 Simple Factory Pattern?

由一個工廠將製作產品的細節隱藏,讓使用者不需要不需要知道細節也能獲得產品
在製作一個物件的時候,物件常常還需要有其他後製的處理,這些處理使用者不需要知道,使用者只需要獲得此產品即可,所以需要將後製處理作封裝

問題情境

要生產 PS5 主機光碟版數位版給使用者,但使用者不需要知道「如何生產 CPU、顯示晶片與加裝光碟機」,使用者只需要獲得此產品就行。
實作有問題的 code 如下:
(相關的 code 在Github - go-design-patterns)
package factory import "fmt" type PS5WithCD struct{} func (p PS5WithCD) PlayGame() { fmt.Println("loading cd...play!") } func (p PS5WithCD) AddCDMachine() { fmt.Println("adding cd machine...done!") } func (p PS5WithCD) AddCPU() { fmt.Println("adding cpu...done!") } func (p PS5WithCD) AddGPU() { fmt.Println("adding gpu...done!") } type PS5WithDigital struct{} func (p PS5WithDigital) PlayGame() { fmt.Println("loading digital file...play!") } func (p PS5WithDigital) AddCPU() { fmt.Println("adding cpu...done!") } func (p PS5WithDigital) AddGPU() { fmt.Println("adding gpu...done!") } func main() { ps5 := PS5WithCD{} ps5.AddCDMachine() ps5.AddCPU() ps5.AddGPU() // or // ps5 := PS5WithDigital{} // ps5.AddCPU() // ps5.AddGPU() ps5.PlayGame() }
使用者有可能會購買光碟版PS5WithCD{}或數位版PS5WithDigital{},並且回家玩遊戲.PlayGame(),但 PS5 得組裝方式.AddCDMachine().AddCPU().AddGPU()都是使用者不需要知道的。
想像一下你去買 PS5 的時候老闆直接在你面前裝 CPU 那畫面也是夠嗆哈哈。

解決方式

我們需要CreatePS5()function 來將 PS5 的製作過程隱藏起來,如下:
package factory import "fmt" type PS5 interface { PlayGame() } type PS5WithCD struct{} func (p PS5WithCD) PlayGame() { fmt.Println("loading cd...play!") } func (p PS5WithCD) AddCDMachine() { fmt.Println("adding cd machine...done!") } func (p PS5WithCD) AddCPU() { fmt.Println("adding cpu...done!") } func (p PS5WithCD) AddGPU() { fmt.Println("adding gpu...done!") } type PS5WithDigital struct{} func (p PS5WithDigital) PlayGame() { fmt.Println("loading digital file...play!") } func (p PS5WithDigital) AddCPU() { fmt.Println("adding cpu...done!") } func (p PS5WithDigital) AddGPU() { fmt.Println("adding gpu...done!") } func CreatePS5(style string) PS5 { switch style { case "PS5WithCD": ps5 := &PS5WithCD{} ps5.AddCDMachine() ps5.AddCPU() ps5.AddGPU() return ps5 case "PS5WithDigital": ps5 := &PS5WithDigital{} ps5.AddCPU() ps5.AddGPU() return &PS5WithDigital{} } return nil } func main() { ps5 := CreatePS5("PS5WithCD") ps5.PlayGame() }
這樣使用者就只需要CreatePS5()後即可直接.PlayGame()CreatePS5()有個特別的地方,
即透過 interface 來定義與外部溝通的方式
由於PS5WithCD{}PS5WithDigital{}始終是不同的東西,我們須將這兩個物件「相同」的行為定義出來,來跟使用者說「這個產品可以玩 PS5 的遊戲」,我們稱這個動作為「抽象」。
所以 PS5 interface 將.PlayGame()這個行為抽象出來,只要能玩 PS5 遊戲的產品,都稱為 PS5 系列相關的產品,即 PS5 的光碟版與數位版。
最後整體 UML 如下(CreatePS5 不是 struct,所以我在上方標註這是一個 function 的區塊):
notion image
PS5WithCD{}PS5WithDigital{}CreatePS5()生產,他們依賴PS5interface,而 user(main function)透過 interface 來操作生產出來的 PS5。