go-優雅的傳遞參數

//type Foo1 struct {
//	num int
//	str string
//}
//
//func New(num int, str string) *Foo1 {
//
//	return &Foo1{
//		num: num,
//		str: str,
//	}
//}

//這種傳參方法弊端很明顯,假設我們需要對Foo內部增加兩個屬性,同時構造函數也需要支持傳入這兩個新增屬性的初始值。有一種修改方法是這樣的:
//func New(num int, str string, num2 int, str2 string)
//可以看到這種方式,隨着初始化參數個數,類型的變化,我們New函數的函數簽名也需隨之改變。這帶來兩個壞處
//1.對調用方來說,函數不兼容
//2.參數數量太多,可讀性可能變差

//一、有一種保持兼容性的解決方案,是保留之前的New函數,再創建一個新的構造函數,比如New2,用於實現4個參數的構造方法
//這種解決方案在大部分時候會導致代碼可維護性下降


//另一種解決方案,是把所有的參數都放入一個結構體種。就像這樣
//type Foo2 struct {
//	option Option2
//}
//
//type Option2 struct {
//	num int
//	str string
//}
//
//func New2(option Option2) *Foo2 {
//	return &Foo2{
//		option: option,
//	}
//}

//這種方式解決了上面提出的兩個問題,但是,假設我們那想爲參數提供默認參數呢
//比如說當調用方不設置num時,我們希望它的默認值是100,不設置str時,默認值爲hello

//func main() {
//	//構造對象時只設置str,不設置num
//	New2(Option2{
//		str: "hello",
//	})
//	//這種做法可行的前提是,屬性的默認值也爲0值
//
//}

//假設我們希望option.num的屬性值是100,那麼當內部收到的option.num=0時,我們沒發區分是調用方希望將option.num設置爲0,還是調
//用方壓根就沒設置option.num。從而導致我們不知道內部的option.num設置爲0好,還是保持默認值100好
//事實上,這個問題不僅僅是傳遞option時纔會出現,即使所有參數都使用最上面那種直接傳遞的方式,也會存在這個問題,即0值無法作爲
//外部是否設置的判斷條件

//有一種解決方法就是指針,如果傳入的爲nil,則使用默認參數

//foo := New2(option2 *Option2)
//func New4(option2 *Option2) *Foo2 {
//	if option2 == nil {
//		//外部沒有設置參數
//設置默認參數
//	}
//
//	return &Foo2{
//		option: option2,
//	}
//}

//改方案的問題是,所有的參數要麼全部由外部傳入,要麼全部使用默認值

//如何優化才能細化到每一個具體的參數,外部設置了使用外部設置的值,外部沒有設置則使用默認值呢?

//五,一種解決方案,是Option中的所有屬性,都使用指針類型,如果特定參數爲nil,則參數使用默認參數,如下
//type Option struct {
//	num *int
//	str *string
//}
//
//type Foo struct {
//	Option
//}

//func New(option Option) *Foo {
//	if option.num == nil {
//		// num默認值
//	} else {
//		//option.num即爲調用方設置的初始值
//	}
//
//	//...
//}

//該方案存在的問題是,對於調用方來說,使用起來有些反人類,因爲你無法使用類似&1的寫法對一個整型字面常量取地址,這意味着調用方必須額外
//定義一個變量保存他需要設置的參數的值,然後再對這個變量取地址賦值給Option的屬性

//六
//另一種值得一提的解決方案,是使用go可變參數的特性
//func New(num int, str string, num2 ...int) {
//	if len(num2) == 0 {
//		//調用方沒有設置num2,內部的num2應使用默認值
//	} else {
//		//num2[0]即爲調用方設置的初始值
//	}
//}

//該方案存在的問題是,只能有一個參數有默認值

//七 開始上主菜了,go是支持頭等函數的語言,即可以將函數作爲變量傳遞。所以我們可以像下面這些寫
//type Option struct {
//	num int
//	str string
//}
//type Foo struct {
//	option Option
//}
//type ModOption func(option *Option)
//
//func New(modOption ModOption) *Foo {
//	option := Option{
//		num: 100,
//		str: "hello",
//	}
//	modOption(&option)
//	return &Foo{
//		option: option,
//	}
//}
//
////我們的New函數不再直接接受Option的值,而是提供了一種類似鉤子函數的功能,使得在內部對option設置完默認值
//func main() {
//	foo := New(func(option *Option) {
//		//調用方只設置 num
//		option.num = 200
//	})
//
//	fmt.Println(foo.option)
//}

//八 那麼假設有些時候,我們覺得某個參數是調用方必須關心的,不應該由內部設置默認值呢?我們可以這樣寫

//type Foo struct {
//	key    string
//	option Option
//
//	//。。。
//}
//
//type Option struct {
//	num int
//	str string
//}
//
//type ModOption func(option *Option)
//
//func New(key string, modOption ModOption) *Foo {
//	option := Option{
//		num: 100,
//		str: "hello",
//	}
//	modOption(&option)
//
//	return &Foo{
//		key:    key,
//		option: option,
//	}
//}
//
//func main() {
//
//	//key關心,必填參數,不需要默認值
//	new := New("iamkey", func(option *Option) {
//		option.num = 200
//	})
//}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章