iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 17
1
自我挑戰組

Let's Eat GO ! 實務開發雜談by Golang系列 第 17

Day17 .[心得與討論篇] 走向interface去設計架構(4)- 思考interface的應用III

重新斟酌內部結構設計

重新再思考一下,外面的struct,裡面是否真的有需要放個struct。

筆者認為,屬於struct內部的變數,放什麼型態都可好處理,除了struct以外,要特別想過自己的佈局,再下決定是不是要擺上struct。

我發覺是這樣的,如果struct內部成員有足以抽象出明顯的『業務』,比起用struct,interface可能會比較合適,而如果只是存放稍微複雜一點的資料,而且真的只是單純作為存放資料用途,可以選擇struct。

如舉例的DB struct來說,Connector是連線,那連線會做很多動作,建立連線呀,連線有問題的處理,連線關閉處理等等,明顯不是個簡簡單單的資料存放單位。

所以是個interface。

那麼我們前一篇舉例的employee(員工)和Tool(工具)呢?Tool算是一種業務嗎?不就只是不同工具?應該算是某種格式的資料儲存?

換個角度想

有兩個問題,筆者認為是要先思考的。

  1. Tool內部的資料對employee很重要嗎?employee需要了解Tool內部的資料嗎?

  2. Tool裡面,為何需要記錄這些資料的,它們的用途在哪裡?

實際上大多時候,外面的往往並不關心裡面的過程,只在乎結果。

這跟現實社會的很多道理是很相像的,你會關心月初能不能拿到薪水,並不在乎發給你薪水的金錢是怎麼流動和處理的。你會關心新上架的遊戲在自己的PS4能不能順利開玩,而不會管這遊戲還需要在PC和XBOX或者Switch一起上架。

工具再怎麼千奇百怪,只要能夠幫你順利完成工作,實際上有些問題你不會管它,譬如說工具價格,它對於老闆可能是個問題,但對員工可沒差別。

所以,Tool在這邊,它的內部資料或規格,對於employee(員工)不是很重要,employee(員工)關心的是工具能不能用,可不可以達到目的,Tool(工具)只要能夠讓employee(員工)可以知道結果即可。

employee(員工)只需要Tool(工具)的method,不需要知道Tool(工具)的struct長怎麼樣。

範例

現在我們就來做個小練習吧,假設工具有其壽命,有使用次數,而且不專屬某位員工特定做使用。就像小學生打掃教室,公用的掃把不會綁定說一定是哪位小朋友專用,小朋友在,掃把在,小朋友畢業了,掃把就不能給其他人使用吧。

因此,第一步,employee裡面不放入Tool的struct,兩個struct沒有耦合關係。

// Tool 工具
type Tool struct {
	id             string
	name           string
	power          float64
	weight         float64
	remainUsedTime int // 剩下可使用次數
}

// Employee 員工
type Employee struct {
	id  int
	age int
}

第二步,工具是人使用的,所以需要把工具遞給員工,但遞出的是哪一把工具不一定。

// UseTool 使用工具
func (e *Employee) UseTool(t *Tool) {

}

第三步,Tool的使用壽命資料是記在Tool的struct裡面,所以這些資訊只有Tool可提供來做檢查。


// UseTool 使用工具
func (e *Employee) UseTool(t *Tool) error {
	if t.remainUsedTime < 0 {
		return errors.New("此工具使用壽命已盡,無法再使用")
	}
	return nil
}

第四步,就如同這幾篇討論的,工具類型可能不同,『t.remainUsedTime』這項資訊可能只有在Tool才有,若來個Tool2狀況就不是這樣了。

// Tool2 工具
type Tool2 struct {
	id       string
	name     string
	power    float64
	weight   float64
	UsedTime int // 使用過的次數
}

// UseTool2 使用工具
func (e *Employee) UseTool2(t *Tool2) error {
	if t.UsedTime >= 10000 {
		return errors.New("此工具使用壽命已盡,無法再使用")
	}
	return nil
}

第五步,可想而知以上的方法不可行,我們要『工具』自己提供好,是不是可以使用的結果,而不是在『員工』的method裡面幫忙做這個判斷。

可以執行,但不可以幫忙判斷。

// IsCanUse 可否使用
func (t *Tool) IsCanUse() bool {
	if t.remainUsedTime < 0 {
		return false
	}
	return true
}

// IsCanUse 可否使用
func (t2 *Tool2) IsCanUse() bool {
	if t2.UsedTime >= 10000 {
		return false
	}
	return true
}

// UseTool 使用工具
func (e *Employee) UseTool(t *Tool) error {
    // 執行判斷
	isOk := t.IsCanUse()
	if !isOk {
		return errors.New("此工具使用壽命已盡,無法再使用")
	}
	return nil
}

第六步,如果傳入的工具參數,先指定好struct,則只能侷限在使用某一種工具,於是改成符合用有我們需要的method就能傳入的interface設計。

// ITool 工具的method規格
type ITool interface {
	IsCanUse() bool
}

// UseTool 使用工具
func (e *Employee) UseTool(t ITool) error {
	isOk := t.IsCanUse()
	if !isOk {
		return errors.New("此工具使用壽命已盡,無法再使用")
	}
	return nil
}

以上,我想就是struct內部會選擇interface型態的變數,道理是這樣來的。


上一篇
Day16 .[心得與討論篇] 走向interface去設計架構(3)- 思考interface的應用II
下一篇
Day18 .[心得與討論篇] 走向interface去設計架構(5)- 從思考資料的想法轉變成思考業務
系列文
Let's Eat GO ! 實務開發雜談by Golang30

尚未有邦友留言

立即登入留言