问题点
当一个函数有很多参数,为了方便函数的使用,我们会给一些参数设定默认值,调用时只需要传与默认值不同的参数即可
问题分析
需求:
上传文件到金山云的 KS3, 上传的时候有很多选择, 如: 文件的 ACL 权限是否公开, 文件的存储类型是否为低频存储或正常存储, 文件的格式是普通文本还是二进制文件等等.
实现
方法1: 每一个选项均作为参数
type options struct {
aclType ACLType
mimeType MIMEType
storageClass StorageClass
header Header
}
func (client *ks3Client) PutObject(objectKey string, reader io.ReadSeeker, aclType ACLType, mimeType MIMEType, storageClass StorageClass) error {
params := &s3.PutObjectInput{
Bucket: aws.String(client.bucket),
Key: aws.String(objectKey),
ACL: aws.String(string(aclType)),
Body: reader,
ContentType: aws.String(string(mimeType)),
StorageClass: aws.String(string(storageClass)),
}
_, err := client.ks3.PutObject(params)
return err
}
|
优点:
简单明了, 非常易于理解
缺点:
不方便扩展, 新增需求原先代码编译错误
参数可能无穷扩展, 调用方代码非常长
无法使用默认值
方法2: 创建配置对象
type Option struct {
aclType ACLType
mimeType MIMEType
storageClass StorageClass
header Header
}
func (client *ks3Client) PutObject(objectKey string, reader io.ReadSeeker, option Option) error {
params := &s3.PutObjectInput{
Bucket: aws.String(client.bucket),
Key: aws.String(objectKey),
ACL: aws.String(string(option.aclType)),
Body: reader,
ContentType: aws.String(string(option.mimeType)),
StorageClass: aws.String(string(option.storageClass)),
}
_, err := client.ks3.PutObject(params)
return err
}
|
优点:
实现简单, 易于理解, 新增选项相对容易
缺点:
必须传 option 这个参数, 即使想使用金山云的默认值
强制方法调用者需要额外创建 option 的对象, 接口不友好
无法使用默认值
方法3 : 配置项作为指针
type options struct {
aclType ACLType
mimeType MIMEType
storageClass StorageClass
header Header
}
func (client *ks3Client) PutObject(objectKey string, reader io.ReadSeeker, options *options) error {
params := &s3.PutObjectInput{
Bucket: aws.String(client.bucket),
Key: aws.String(objectKey),
ACL: aws.String(string(options.aclType)),
Body: reader,
ContentType: aws.String(string(options.mimeType)),
StorageClass: aws.String(string(options.storageClass)),
}
_, err := client.ks3.PutObject(params)
return err
}
|
优点:
可以直接传一个 nil 进去
缺点:
api 很奇怪, 传一个 nil
无法使用默认值
方法4: 选项模式
type Option func(*options)
type options struct {
aclType ACLType
mimeType MIMEType
storageClass StorageClass
header Header
}
func WithACLType(aclType ACLType) Option {
return func(o *options) {
o.aclType = aclType
}
}
func WithMIMEType(mimeType MIMEType) Option {
return func(o *options) {
o.mimeType = mimeType
}
}
func WithStorageClass(storageClass StorageClass) Option {
return func(o *options) {
o.storageClass = storageClass
}
}
func GetHeaderMeta(header Header) Option {
return func(o *options) {
o.header = header
}
}
func (client *ks3Client) PutObject(objectKey string, reader io.ReadSeeker, option ...Option) error {
client.options = defaultPutOption
for _, opt := range option {
opt(&client.options)
}
params := &s3.PutObjectInput{
Bucket: aws.String(client.bucket),
Key: aws.String(objectKey),
ACL: aws.String(string(client.options.aclType)),
Body: reader,
ContentType: aws.String(string(client.options.mimeType)),
StorageClass: aws.String(string(client.options.storageClass)),
}
_, err := client.ks3.PutObject(params)
return err
}
|
优点:
可以使用默认值, 只需要传非默认值以外的值
可以校验传入的值
缺点:
实现较复杂, 使用闭包