Functional Options

问题点

当一个函数有很多参数,为了方便函数的使用,我们会给一些参数设定默认值,调用时只需要传与默认值不同的参数即可

问题分析

需求:

上传文件到金山云的 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
}

优点: 

可以使用默认值, 只需要传非默认值以外的值

可以校验传入的值

缺点:

实现较复杂, 使用闭包

猜你喜欢

转载自www.cnblogs.com/Zereker/p/11396647.html
今日推荐