Go 函数选项模式(Functional Options Pattern)

2023-11-02 23:19:26 浏览数 (1)

前言

在日常开发中,有些函数可能需要接收许多参数,其中一些参数是必需的,而其他参数则是可选的。当函数参数过多时,函数会变得臃肿且难以理解。此外,如果在将来需要添加更多参数,就必须修改函数签名,这将影响到已有的调用代码。

而函数选项模式(functional options)的出现解决了这个问题,本文将对其进行讲解,准备好了吗?准备一杯你最喜欢的咖啡或茶,随着本文一探究竟吧。

函数选项模式

什么是函数选项模式

Go 语言中,函数选项模式是一种优雅的设计模式,用于处理函数的可选参数。它提供了一种灵活的方式,允许用户在函数调用时传递一组可选参数,而不是依赖于固定数量和顺序的参数列表。

函数选项模式的好处

  • 易于使用:调用者可以选择性的设置函数参数,而不需要记住参数的顺序和类型;
  • 可读性强:函数选项模式的代码有着自文档化的特点,调用者能够直观地理解代码的功能;
  • 扩展性好:通过添加新的 Option 参数选项,函数可以方便地扩展功能,无需修改函数的签名;
  • 函数选项模式可以提供默认参数值,以减少参数传递的复杂性。

函数选项模式的实现

函数选项模式的实现一般包含以下几个部分:

  • 选项结构体:用于存储函数的配置参数
  • 选项函数类型:接收选项结构体参数的函数
  • 定义功能函数:接收 0 个或多个固定参数和可变的选项函数参数
  • 设置选项的函数:定义多个设置选项的函数,用于设置选项

代码示例:

代码语言:go复制
type Message struct {
   // 标题、内容、信息类型
   title, message, messageType string

   // 账号
   account     string
   accountList []string

   // token
   token     string
   tokenList []string
}

type MessageOption func(*Message)

func NewMessage(title, message, messageType string, opts ...MessageOption) *Message {
   msg := &Message{
      title:       title,
      message:     message,
      messageType: messageType,
   }

   for _, opt := range opts {
      opt(msg)
   }

   return msg
}

func WithAccount(account string) MessageOption {
   return func(message *Message) {
      message.account = account
   }
}

func WithAccountList(accountList []string) MessageOption {
   return func(message *Message) {
      message.accountList = accountList
   }
}

func WithToken(token string) MessageOption {
   return func(message *Message) {
      message.token = token
   }
}

func WithTokenList(tokenList []string) MessageOption {
   return func(message *Message) {
      message.tokenList = tokenList
   }
}

func main() {
   // 单账号推送
   _ = NewMessage(
      "来自陈明勇的信息",
      "你好,我是陈明勇",
      "单账号推送",
      WithAccount("123456"),
   )

   // 多账号推送
   _ = NewMessage(
      "来自陈明勇的信息",
      "你好,我是陈明勇",
      "多账号推送",
      WithAccountList([]string{"123456", "654321"}),
   )
}

上述例子中,使用了函数选项模式来创建 Message 结构体,并根据消息类型配置不同消息的属性。

首先定义了 Message 结构体,其包含 7 个字段;

其次定义 MessageOptionm选项函数类型,用于接收 Message 参数的函数;

再次定义 NewMessage 函数,用于创建一个 Message 指针变量,在 NewMessage 函数中,固定参数包括 titlemessagemessageType,它们是必需的参数。然后,通过可选参数 opts ...MessageOption 来接收一系列的函数选项;

然后定义了四个选项函数:WithAccountWithAccountListWithTokenWithTokenList。这些选项函数分别用于设置被推送消息的账号、账号列表、令牌和令牌列表。

最后,在 main 函数中,展示了两种不同的用法。第一个示例是创建单账号推送的消息,通过调用 NewMessage 并传递相应的参数和选项函数(WithAccount)来配置消息。第二个示例是创建多账号推送的消息,同样通过调用 NewMessage 并使用不同的选项函数(WithAccountList)来配置消息。

这种使用函数选项模式的方式可以根据需要消息类型去配置消息的属性,使代码更具灵活性和可扩展性。

函数选项模式的缺点

前面提到了函数选项模式的优势(好处),但也必须承认它存在一些缺点。

  • 复杂性:函数选项模式引入了更多的类型和概念,需要更多的代码和逻辑来处理。这增加了代码的复杂性和理解的难度,尤其是对于初学者来说。
  • 可能存在错误的选项组合:由于函数选项模式允许在函数调用中指定多个选项,某些选项之间可能存在冲突或不兼容的情况。这可能导致意外的行为或错误的结果。
  • 不适用于所有情况:函数选项模式适用于有大量可选参数或者可配置选项的函数,但对于只有几个简单参数的函数,使用该模式可能过于复杂和冗余。在这种情况下,简单的命名参数可能更直观和易于使用。

小结

本文对 Go 函数选项模式(Functional Options Pattern)进行了详细介绍,并通过封装一个消息结构体的例子,展示了如何使用函数选项模式进行代码实现。

在合适的情况下,我们可以使用函数选项模式来封装一些功能,定制函数的行为,提高代码的可读性和可扩展性。

你是否在实际开发中使用过函数选项模式?欢迎评论区留言探讨。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞