问题点
当一个函数有很多参数,为了方便函数的使用,我们会给一些参数设定默认值,调用时只需要传与默认值不同的参数即可
问题分析
需求:
上传文件到金山云的 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 } |
优点:
可以使用默认值, 只需要传非默认值以外的值
可以校验传入的值
缺点:
实现较复杂, 使用闭包