# Go Functional Options(Go 函数式选项)


## [函数式编程](https://zh.wikipedia.org/zh-cn/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B)：

### 定义：

&gt;   **函数式编程**，或称**函数程序设计**、**泛函编程**（英语：Functional programming），是一种[编程范型](https://zh.wikipedia.org/wiki/编程范型)，它将[电脑运算](https://zh.wikipedia.org/wiki/電腦運算)视为[函数](https://zh.wikipedia.org/wiki/函数)运算，并且避免使用程序[状态](https://zh.wikipedia.org/w/index.php?title=状态_(计算机科学)&amp;action=edit&amp;redlink=1)以及[可变物件](https://zh.wikipedia.org/wiki/不可變物件)。		–维基百科

### 简介：

&gt;   在函数式编程中，函数是[头等对象](https://zh.wikipedia.org/wiki/头等对象)即[头等函数](https://zh.wikipedia.org/wiki/头等函数)，这意味着一个函数，既可以作为其它函数的输入参数值，也可以从函数中返回值，被修改或者被分配给一个变量。[λ演算](https://zh.wikipedia.org/wiki/Λ演算)是这种范型最重要的基础，λ演算的函数可以接受函数作为输入参数和输出返回值。
&gt;
&gt;   比起[指令式编程](https://zh.wikipedia.org/wiki/指令式編程)，函数式编程更加强调程序执行的结果而非执行的过程，倡导利用若干简单的执行单元让计算结果不断渐进，逐层推导复杂的运算，而不是设计一个复杂的执行过程。		–维基百科

### 总结：

函数式编程：

1. 编程范型
1. 函数既可以作为输入参数也可以作为返回值
1. 注重结果而非过程
1. 倡导化繁为简

## 函数式选项模式：

函数式选项模式代码实现框架：

1.   定义一个`Client`struct

     ```go
     // Client结构体
     type Client struct {
     	// 主机名
     	host string
     
     	// 端口
     	port int
     
     	// 是否使用ssl
     	ssl bool
     
     	// tlsconfig配置
     	tlsconfig *tls.Config
     
     	......
     }
     ```

     

2.   定义一个`Option`函数类型

     ```go
     // 定义 Option 是一个`func(*Client)` 函数类型
     type Option func(*Client)
     ```

     

3.   定义`NewClient()`函数

     ```go
     // 定义NewClient函数，返回一个`Client`对象和错误，第一个参数为host，剩下的为Option类型的参数
     func NewClient(h string, o ...Option) *Client {
         
         // 用于创建一个默认的Client
     	c := &amp;Client{
     		host:      h,
     		port:      DefaultPort,
     		tlsconfig: &amp;tls.Config{ServerName: h, MinVersion: DefaultTLSMinVersion},
     		tlspolicy: DefaultTLSPolicy,
             ......
     	}
     	
         // 遍历Option函数切片，得到一个Option函数
     	for _, co := range o {
             // 对Option函数进行判空
     		if co == nil {
     			continue
     		}
             // 执行Option函数
             co(c)
     	}
     	
         // 返回Client对象
     	return c
     }
     ```

     

4.   定义`Option`函数

     ```go
     // 定义一个配置端口函数，传入配置项的值，返回Option函数
     func WithPort(p int) Option {
     	return func(c *Client) {
             // 应用配置
     		c.port = p
     	}
     }
     
     func WithSSL() Option {
     	return func(c *Client) {
     		c.ssl = true
     	}
     }
     
     func WithTLSConfig(co *tls.Config) Option {
     	return func(c *Client) {
     		c.tlsconfig = co
     	}
     }
     ......
     ```

     

5.   创建`Client`对象

     ```go
     c := NewClient(
     		&#34;hiif.ong&#34;,
     		WithSSL(),
     		WithPort(8089),
             WithTLSConfig(&amp;tls.Config{
     			ServerName:         config.MAIL.Host,
     			InsecureSkipVerify: true,
             }),
     	)
     ```

     我相信看过开源库的应该会对这个函数式选项模式有一点印象。

看了网上很多讲函数式选项模式的文章都没有看到讲到与函数式编程的联系，然后我这里引用了维基百科对函数式编程的定义及简介，我认为函数式选项模式跟函数式编程还是有一定的联系的，第一点New函数使用Option函数类型作为输入参数；第二点Option函数以Option函数类型作为返回值；第三点化繁为简，将单独的一个配置函数抽离，单独编写；等等。

欢迎各位大佬批评指正。

### 本文的代码参考[go-mail](https://github.com/wneessen/go-mail)开源库，[本文函数式选项模式的完整代码](https://github.com/wneessen/go-mail/blob/main/client.go)



---

> 作者: hiifong  
> URL: https://f.style/functional-options/  

