下面将记录函数可选(functional options)模式在golang中的实现。
函数可选参数模式(或者可选参数模式、Optional Parameters Pattern),用在当构造函数和公共函数API需要可选参数,特别时当具有三个或者更多可选参数时。
这个模式的优势在于,可以实现一个方法,并用下面的简单方式进行调用,如:
obj . Method ( mandatory1 , mandatory2 )
或者向下面这样,通过是用可选的参数,来改变它的行为:
obj . Method ( mandatory1 , mandatory2 , option1 , option2 , option3 )
这可以避免为可选参数,使用笨重的零值参数:
obj . Method ( mandatory1 , mandatory2 , nil , "" , 0 )
或者使用同样笨重的使用配置对象的方式:
obj . Method ( mandatory1 , mandatory2 , & cfg )
使用一个Option接口,该接口保存一个未导出的方法,同时在一个未导出options结构中记录可选的参数信息。
func ( c cacheOption) apply ( opts * options){
func WithCache ( c bool )Option{
func ( l limitOption) apply ( opts * options){
func WithLimit ( limit int )Option{
return limitOption ( limit )
type loggerOption struct {
func ( l loggerOption) apply ( opts * options){
func WithLogger ( log * zap.Logger) Option{
return loggerOption{ logger : log }
函数:
func Open ( addr string , opts ... Option)( * Connection, error ){
使用:
db . Open ( add , db . WithLimit ( 10 ))
db . Open ( addr , db . WithLogger ( log ))
db . Open ( addr , db . WithCache ( 1 ), db . WithLimit ( 10 ), db . WithLogger ( log ))
将Option定义为函数类型,使用闭包来实现
type Option func ( * options)
func WitchCache ( cache bool )Option{
return func ( opts * options){
func WithLimit ( limit int ) Option {
return func ( opts * options){
func WithLogger ( log * zap.Logger) Option {
return func ( opts * options) {
函数:
func Open ( addr string , opts ... Option)( * Connection, error ){
使用
db . Open ( add , db . WithLimit ( 10 ))
db . Open ( addr , db . WithLogger ( log ))
db . Open ( addr , db . WithCache ( 1 ), db . WithLimit ( 10 ), db . WithLogger ( log ))
type DailOption interface {
type funcDialOption struct {
func ( fdo * funcDialOption) apply ( do * dailOption) {
func newFuncDialOption ( f func ( o * dailOption)) * funcDialOption {
return & funcDialOption{ f : f }
func WithDisableRetry () DailOption {
return newFuncDialOption ( func ( o * dailOption) {
函数:
// Dial creates a client connection to the given target.
func Dial ( target string , opts ... DialOption) ( * ClientConn, error ) {
return DialContext ( context . Background (), target , opts ... )
使用:
grpc . Dial ( " localhost:8080 " )
grpc . Dial ( " localhost:8080 " , grpc . WithInsecure (), grpc . WithBlock ())
grpc . Dial ( " localhost:8080 " , grpc . WithDisableRetry ())
方式3中,需要将具体的配置选项暴露出来,而这种方式不用。
通用方式实现一个可复用组件,来实现一个具有下面形式参数的函数:
obj . Method ( mandatory1 , mandatory2 , option1 , option2 , option3 )
在内部,只需要像下面这样声明定义该方法:
func ( obj * Object) Method ( m1 Type1, m2 Type2, options ... Option) {
Option对象有两个部分,一个标识和一个值。标识和值都被声明成interface{},这样标识和值都可以是任意的数据类型。对于标识,通常最好使用一个未导出的空结构,如:
// Interface defines the minimum interface that an option must fulfill
// Ident returns the "indentity" of this option, a unique identifier that
// can be used to differentiate between options
// Value returns the corresponding value.
// New creates a new Option
func New ( ident , value interface {}) Option {
func ( p * pair) Ident () interface {} {
func ( p * pair) Value () interface {} {
type identOptionalParamOne struct {}
type identOptionalParamTwo struct {}
type identOptionalParamThree struct {}
func WithOptionOne ( v ... ) Option {
return option . New (identOptionalParamOne{}, v )
然后,可以通过下面的方式,调用上面定义的Method方法:
obj . Method ( m1 , m2 , WithOptionOne ( ... ), WithOptionTwo ( ... ), WithOptionThree ( ... ))
同时Method的options参数,需要用类似下面的方式进行解析:
func ( obj * Object) Method ( m1 Type1, m2 Type2, options ... Option) {
paramOne := defaultValueParamOne
for _ , option := range options {
case identOptionalParamOne {}:
paramOne = option . Value ().( ... )