Go语言实践模式 - 函数选项模式(Functional Options Pattern)
什么是函数选项模式
大家好,我是小白,有点黑的那个白。
最近遇到一个问题,因为业务需求,需要对接三方平台.
而三方平台提供的一些HTTP(S)接口都有统一的密钥生成规则要求.
为此我们封装了一个独立的包 xxx-go-sdk 以便维护和对接使用.
其中核心的部分是自定义HTTP Client,如下:
type Client struct {}
func (c *Client) do() {
// 实现统一的加密和签名逻辑
// 统一调用net/http
}
// 订单列表接口
func (c *Client) OrderList(){
c.do()
}
// 订单发货接口
func (c *Client) OrderDelivery(){
c.do()
}
// ... 其他接口
一些平台会要求appKey/appSecret等信息,所以Client结构体就变成了这样,这时参数还比较少, 而且是必填的参数,我们可以提供构造函数来明确指定。
type Client struct {
AppKey string
AppSecret string
}
func NewClient(appKey string, appSecret string) *Client {
c := new(Client)
c.AppKey = appKey
c.AppSecret = appSecret
return c
}
看起来很满足,但是当我们需要增加一个 Timeout 参数来控制超时呢?
或许你会说这还不简单,像下面一样再加一个参数呗
type Client struct {
AppKey string
AppSecret string
Timeout time.Duration
}
func NewClient(appKey string, appSecret string, timeout time.Duration) *Client {
c := new(Client)
c.AppKey = appKey
c.AppSecret = appSecret
c.Timeout = timeout
return c
}
那再加些其他的参数呢?那构造函数的参数是不是又长又串,而且每个参数不一定是必须的,有些参数我们有会考虑默认值的问题。
为此,勤劳但尚未致富的 gophers 们使用了总结一种实践模式
首先提取所有需要的参数到一个独立的结构体 Options,当然你也可以用 Configs 啥的.
type Options struct {
AppKey string
AppSecret string
}
然后为每个参数提供设置函数
func WithAppKey(appKey string) func(*Options) {
return func(o *Options) {
o.AppKey = appKey
}
}
func WithAppSecret(appSecret string) func(*Options) {
return func(o *Options) {
o.AppSecret = appSecret
}
}
这样我们就为每个参数设置了独立的设置函数。返回值 func(*Options)
看着有点不友好,我们提取下定义为单个 Option
调整一下代码
type Option func(*Options)
func WithAppKey(appKey string) Option {
return func(o *Options) {
o.AppKey = appKey
}
}
func WithAppSecret(appSecret string) Option {
return func(o *Options) {
o.AppSecret = appSecret
}
}
当我们需要添加更多的参数时,只需要在 Options 添加新的参数并添加新参数的设置函数即可。
比如现在要添加新的参数 Timeout
type Options struct {
AppKey string
AppSecret string
Timeout. time.Duration // 新增参数
}
// Timeout 的设置函数
func WithTimeout(timeout time.Duration) Option {
return func(o *Options) {
o.Timeout = timeout
}
}
这样后续不管新增多少参数,只需要新增配置项并添加独立的设置函数即可轻松扩展,并且不会影响原有函数的参数顺序和个数位置等。
至此,每个选项是区分开来了,那么怎么作用到我们的 Client 结构体上呢?
首先,配置选项都被提取到了 Options 结构体中,所以我们需要调整一下 Client 结构体的参数
type Client struct {
options *Options
}
其次,每一个选项函数返回 Option,那么任意多个就是 ...Option,我们调整一下构造函数 NewClient 的参数形式,改为可变参数,不在局限于固定顺序的几个参数。
func NewClient(options ...Option) *Client {
c := new(Client)
c.Options = ?
return c
}
然后循环遍历每个选项函数,来生成Client结构体的完整配置选项。
func NewClient(options ...Option) *Client {
opts := new(Options)
for _, o := range options {
o(opts)
}
c := new(Client)
c.Options = opts
return c
}
那么怎么调用呢?对于调用方而已,直接在调用构造函数NewClient()的参数内添加自己需要的设置函数(WithXXX)即可
client := NewClient(
WithAppKey("your-app-key"),
WithAppSecret("your-app-secret"),
)
当需要设置超时参数,直接添加 WithTimeout即可,比如设置3秒的超时
client := NewClient(
WithAppKey("your-app-key"),
WithAppSecret("your-app-secret"),
WithTimeout(3*time.Second),
)
配置选项的位置可以任意设置,不需要受常规的固定参数顺序约束。
可以看到,这种实践模式主要作用于配置选项,利用函数支持的特性来实现的,为此得名 Functional Options Pattern,优美的中国话叫做「函数选项模式」。
总结
最后, 我们总结回顾一下在Go语言中函数选项模式的优缺点
优点
- 支持多参数;
- 支持参数任意位置顺序;
- 支持默认值设置;
- 向后兼容,扩展性极佳;
- 用户使用行为一致, 体感良好.
缺点
这是特性,不是缺点 - -!
- 增加了Options结构和Option定义;
- 针对每个参数都有对应的设置函数,每个选项函数的实现代码量好像多了一些;
Go语言实践模式 - 函数选项模式(Functional Options Pattern)的更多相关文章
- Go语言设计模式之函数式选项模式
Go语言设计模式之函数式选项模式 本文主要介绍了Go语言中函数式选项模式及该设计模式在实际编程中的应用. 为什么需要函数式选项模式? 最近看go-micro/options.go源码的时候,发现了一段 ...
- golang中的选项模式
索引 https://waterflow.link/articles/1663835071801 当我在使用go-zero时,我看到了好多像下面这样的代码: ... type ( // RunOpti ...
- 基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传
在基于SqlSugar的开发框架的服务层中处理文件上传的时候,我们一般有两种处理方式,一种是常规的把文件存储在本地文件系统中,一种是通过FTP方式存储到指定的FTP服务器上.这种处理应该由程序进行配置 ...
- Golang 常见设计模式之选项模式
熟悉 Python 开发的同学都知道,Python 有默认参数的存在,使得我们在实例化一个对象的时候,可以根据需要来选择性的覆盖某些默认参数,以此来决定如何实例化对象.当一个对象有多个默认参数时,这个 ...
- 【读书笔记】读《JavaScript模式》 - 函数复用模式之现代继承模式
现代继承模式可表述为:其他任何不需要以类的方式考虑得模式. 现代继承方式#1 —— 原型继承之无类继承模式 function object(o) { function F() {}; F.protot ...
- 【读书笔记】读《JavaScript模式》 - JavaScript函数常用模式
API模式:回调模式.配置对象.返回函数: 初始化模式:即时函数.即时对象初始化.初始化分支: 性能模式:备忘模式.自定义模式 //*********************** API模式 **** ...
- javascript优化--05模式(函数)
回调函数模式: 基本例子: var findNodes = function (callback) { ...................... if (typeof callback !== ' ...
- javascript创建对象之函数构造模式和原型模式结合使用(四)
创建自定义类型的常见方式就是组合使用构造函数模式与原型模式一起使用. 构造函数模式用于定义实例对象的特有的部分(属性和方法),原型模式用于定义共享的部分. 这样最大限度的节省了内存的开销. funct ...
- ASP.NET Core 2.2 基础知识(七) 选项模式
承接上一篇 配置, 选项模式是专门用类来表示相关配置的服务. 基本选项配置 新建一个选项类,该类必须是包含无参数的构造函数的非抽象类. public class MyOptions { public ...
随机推荐
- Prometheus由于时间不同步导致数据不显示
原文链接:Prometheus由于时间不同步导致数据不显示 问题 部署 prometheus 后,访问前端界面发现: 这是由于你windows机器与部署prometheus服务器的时间不同步导致的. ...
- 查找bug的一些经验总结
项目开发中遇到的bug解决经验总结 今天在项目开发中遇到了两个很难解决的bug,我把我的思路记录下来,以供之后遇到bug时,提供一些思路: 编译通过,但总结"core dumped" ...
- synchronized是对象锁还是全局锁
昆昆欧粑粑 2019-02-20 15:09:59 1148 收藏 1分类专栏: java学习 文章标签: synchronized 全局锁 对象锁 同步版权都可以锁!synchronized(thi ...
- Oracle SQL Developer.exe双击启动错误信息dll未找到
下载地址:https://www.oracle.com/tools/downloads/sqldev-downloads.html 官网相应的解决方法已经说明了
- Spring支持的事务管理类型?
Spring支持两种类型的事务管理: 编程式事务管理 :这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护. 声明式事务管理: 这意味着你可以将业务代码和事务管理分离,你只需用注解和XM ...
- JavaScript的一些实用操作(逐步添加)
1.js代码简洁高效计时 console.time('a'); //记录时间开始 ... console.timeEnd('a'); //记录时间结束 a: 12857.81103515625ms / ...
- 什么是 AOP?
在软件开发过程中,跨越应用程序多个点的功能称为交叉问题.这些交叉问题与 应用程序的主要业务逻辑不同.因此,将这些横切关注与业务逻辑分开是面向方 面编程(AOP)的地方.
- spring-boot-learning-Web开发知识
1).创建SpringBoot应用,选中我们需要的模块: 2).SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来 3).自己编写业务代码: 文件名的功能 x ...
- volatile 能使得一个非原子操作变成原子操作吗?
一个典型的例子是在类中有一个 long 类型的成员变量.如果你知道该成员变量 会被多个线程访问,如计数器.价格等,你最好是将其设置为 volatile.为什么? 因为 Java 中读取 long 类型 ...
- 滑动窗口法——Leetcode例题
滑动窗口法--Leetcode例题(连更未完结) 1. 方法简介 滑动窗口法可以理解为一种特殊的双指针法,通常用来解决数组和字符串连续几个元素满足特殊性质问题(对于字符串来说就是子串).滑动窗口法的显 ...