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
}

优点: 

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

可以校验传入的值

缺点:

实现较复杂, 使用闭包

 

posted @ 2019-08-22 20:28  Zereker  阅读(225)  评论(0编辑  收藏  举报