iT邦幫忙

2021 iThome 鐵人賽

DAY 27
0
Software Development

什麼都不會還敢說你是 RD 啊?畢業後的後端入職前準備系列 第 27

【Day 27】Design Patterns with Go I:Simple Factory / Factory / Abstract Factory

剛學一點 Go,
除了能夠使用別人寫好的 modules,
為了程式碼的可擴充性,也需要理解 Design Patterns。
今天搭配 Github 上的程式碼來學習各種「工廠」的設計模式~

Design Patterns

大一剛學 Java 時,學到一些 interface 的概念,
也接觸到 Design Patterns 的書,
但實在難以理解製造出會叫的鴨子跟未來寫網頁、寫應用程式的關聯?

設計模式,是別人研究出來,怎麼樣寫程式能達到好擴充的方法。
因為需求會變動,
如果新增一個功能就要增加非常多行程式碼,
或是新增程式碼常常導致舊功能出錯,
十分浪費時間心力。

我覺得學習 Design Patterns 不是一件容易的事,
運用到很多抽象化的概念。
而就算理解一個設計模式,
接下來要能夠融會貫通的使用在適合的情境,除了經驗也需要刻意留心。

本篇參考這個 Github repo 的程式碼:Go 语言设计模式 來理解設計模式。
而這本 Gitbook 設計模式學習筆記 講解前面幾個模式也算是簡單易懂,並附上類別圖和程式碼,值得參考。

注意 Go 裡面沒有 object / class,
這邊如果提到物件通常是 Go 中 implement interface 的 struct

Simple Factory

根據傳入的參數回傳指定物件

package main
import "fmt"

type Animal interface {
	Say() string
}

func newAnimal(t int) Animal {
	// 如果想增加不同動物,需要在這裡改
	// 不符合 SOLID 的 O(開放封閉原則)
	if t == 1 {
		return Dog{}
	} else if t == 2{
		return &Cat{}
	}
	return nil
}
type Cat struct {}
type Dog struct {}

func (*Cat) Say() string {
	return "meow!"
}

func (Dog) Say() string {
	return "bark!"
}

func main() {
	a1 := newAnimal(1)
	fmt.Println("type 1 Animal")
	fmt.Println(a1.Say())
	a2 := newAnimal(2)
	fmt.Println("type 2 Animal")
	fmt.Println(a2.Say())	
}

Factory Pattern

規定一個工廠該有的樣子(interface),要能夠製造不同商品(instance)就各自去實現不同的工廠

repo 中的程式碼以 Operator 來舉例。04_factory_method

  • 情境舉例:對於一個二元運算子(加減乘除等)來說,都是對於兩個數字做操作的結果。把不同二元運算子都有的資料(兩個數字)和方法(設定A、設定B、AB透過運算子得到的結果)抽象出來,定義新的二元運算子時不容易出錯。

OperatorFactory 是工廠 interface,合格的工廠應該要實作一個會製造出 OperatorCreate method。
Operator 則是定義了 SetA SetB Result 三個 method。
SetA SetB 都是對 struct OperatorBase 定義的 method,OperatorBase 中有 a b 兩整數。

提醒 Go 中沒有繼承,就用 nested struct 達到類似效果:

type PlusOperator struct {
	*OperatorBase
}

Abstract Factory

工廠產生的物件「有關聯」

完整程式碼:05_abstract_factory

  • 情境舉例:要存訂單一定要有主紀錄與詳情紀錄。但有時可能用 RDB(指的是關連式資料庫),有時候想存 XML。

Order interface

DAOFactory 是抽象工廠的 interface,要實作這個工廠,底下就要使用 CreateOrderMainDAO()CreateOrderDetailDAO()分別實作兩個子工廠 OrderMainDAOOrderDetailDAO
這裏就存有訂單主紀錄與詳情紀錄的抽象關係。

type OrderMainDAO interface {
	SaveOrderMain()
}
type OrderDetailDAO interface {
	SaveOrderDetail()
}

type DAOFactory interface {
	CreateOrderMainDAO() OrderMainDAO
	CreateOrderDetailDAO() OrderDetailDAO
}

RDB 工廠實作

RDB 工廠的實作環節,
首先先做一個 RDB 抽象工廠,
裡面要去實作 使用 RDB 儲存主紀錄和詳情紀錄的實現方式。

XML 的工廠實作也一樣,就是 method 裡面的實作細節不同而已

// 使用 RDB 存主紀錄
type RDBMainDAO struct{}
func (*RDBMainDAO) SaveOrderMain() {
	fmt.Print("rdb main save\n")
}
// 使用 RDB 存詳情紀錄
type RDBDetailDAO struct{}
func (*RDBDetailDAO) SaveOrderDetail() {
	fmt.Print("rdb detail save\n")
}

// RDB 的抽象工廠與實現
type RDBDAOFactory struct{}

func (*RDBDAOFactory) CreateOrderMainDAO() OrderMainDAO {
	return &RDBMainDAO{}
}
func (*RDBDAOFactory) CreateOrderDetailDAO() OrderDetailDAO {
	return &RDBDetailDAO{}
}

上一篇
【Day 26】Go 與 Redis
下一篇
【Day 28】Design Patterns with Go II:Prototype, Singleton, Facade
系列文
什麼都不會還敢說你是 RD 啊?畢業後的後端入職前準備31

尚未有邦友留言

立即登入留言