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
//	})
//}
发布了43 篇原创文章 · 获赞 37 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_28119741/article/details/103419193