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