原创

Go语言函数式选项模式

温馨提示:
本文最后更新于 2020年07月12日,已超过 1,594 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

普通的结构体模式存在的缺点

普通模式需要输入相应的参数,但是有的时候我们有默认参数的,所以普通模式不灵活。

// student结构体
type StudentModel struct {
    name   string
    school string
    age    int
}

// 构建结构体,必须输入对应参数
func NewStudentModel(name, school string, age int) *StudentModel {
    return &StudentModel{
        name:   name,
        school: school,
        age:    age,
    }
}

func NewStudentModel2(school string, age int) *StudentModel {
    return &StudentModel{
        // 这样子未免太蹩脚了
        name:   "会打篮球的程序猿",
        school: school,
        age:    age,
    }
}

// 构建结构体,这种方式不能修改默认属性,不够灵活
func NewStudentDefault() *Student {
    return &Student{
        name:   "lzhpo",
        school: "北京大学",
        age:    20,
    }
}

函数式选项模式运用到的知识点?

闭包(闭包 = 匿名函数 + 引用环境):一个持有外部环境变量的函数就是闭包。

函数式选项模式的优点?

可以根据需要,灵活的设置结构体的值。

Go语言函数式选项模式-示例Demo

basketball.go

package structs

// 定义一个篮球结构体
type BasketBall struct {
    Name string
    Color string
    Price float64
}

// 针对篮球结构体定义一个函数类型
type BasketBallFunc func(*BasketBall)

func WithName(name string) BasketBallFunc {
    return func(basketBall *BasketBall) {
        basketBall.Name = name
    }
}

func WithColor(color string) BasketBallFunc {
    return func(basketBall *BasketBall) {
        basketBall.Color = color
    }
}

func WithPrice(price float64) BasketBallFunc {
    return func(basketBall *BasketBall) {
        basketBall.Price = price
    }
}

// 结构体默认实例
var (
    basketBallDefault = BasketBall{
        Name: "斯伯丁比赛专用球",
        Color: "黄色",
        Price: 300.99,
    }
)

// 创建一个构造函数,传入的是我定义的函数类型
func NewBasketBall(opts ...BasketBallFunc) *BasketBall {
    // 默认值,这里也可以抽一个默认值的实例,直接引用也可以
    //temp := BasketBall{
    //    name: "斯伯丁比赛专用球",
    //    color: "黄色",
    //    price: 300.99,
    //}

    temp := basketBallDefault

    // 遍历,并对每一项应用返回的闭包到结构体的选项变量
    for _, opt := range opts {
        opt(&temp)
    }

    return &temp
}

main.go

package main

import (
    "fmt"
    "study-golang/function-model/model-demo3/structs"
)

/*
Go语言函数式选项模式:

为什么要使用函数式选项模式?
- 普通模式需要输入相应的参数,但是有的时候我们有默认参数的,所以普通模式不灵活。

函数式选项模式运用到的知识点?
- 闭包(闭包 = 匿名函数 + 引用环境):一个持有外部环境变量的函数就是闭包。

函数式选项模式的优点?
- 可以根据需要,灵活的设置结构体的值。

函数式选项模式设计参考来源,go-micro源码:https://github.com/micro/go-micro/blob/master/options.go
 */
func main() {
    fmt.Println(structs.NewBasketBall())

    fmt.Println(structs.NewBasketBall(structs.WithName("李宁比赛专用球")))

    fmt.Println(structs.NewBasketBall(
        structs.WithName("李宁比赛专用球"), structs.WithColor("白色")))

    fmt.Println(structs.NewBasketBall(
        structs.WithName("李宁比赛专用球"), structs.WithColor("白色"), structs.WithPrice(129.99)))
}

运行结果:

&{斯伯丁比赛专用球 黄色 300.99}
&{李宁比赛专用球 黄色 300.99}
&{李宁比赛专用球 白色 300.99}
&{李宁比赛专用球 白色 129.99}

函数式选项模式应用-go-micro源码

https://github.com/micro/go-micro/blob/master/options.go

里面就用到了函数式选项模式

options.go源代码解析:

go-micro里面的options的函数式选择模式.png

package micro

import (
    "context"
    "go-micro/go-micro-master/config/source/cli"
    "time"

    "github.com/micro/cli/v2"
    "github.com/micro/go-micro/v2/auth"
    "github.com/micro/go-micro/v2/broker"
    "github.com/micro/go-micro/v2/client"
    "github.com/micro/go-micro/v2/config"
    "github.com/micro/go-micro/v2/config/cmd"
    "github.com/micro/go-micro/v2/debug/profile"
    "github.com/micro/go-micro/v2/debug/trace"
    "github.com/micro/go-micro/v2/registry"
    "github.com/micro/go-micro/v2/router"
    "github.com/micro/go-micro/v2/runtime"
    "github.com/micro/go-micro/v2/selector"
    "github.com/micro/go-micro/v2/server"
    "github.com/micro/go-micro/v2/store"
    "github.com/micro/go-micro/v2/transport"
)

/*

【函数式选项设计模式】

 */

// 服务注册结构体
// Options for micro service
type Options struct {
    Auth      auth.Auth
    Broker    broker.Broker
    Cmd       cmd.Cmd
    Config    config.Config
    Client    client.Client
    Server    server.Server
    Store     store.Store
    Registry  registry.Registry
    Router    router.Router
    Runtime   runtime.Runtime
    Transport transport.Transport
    Profile   profile.Profile

    // Before and After funcs
    BeforeStart []func() error
    BeforeStop  []func() error
    AfterStart  []func() error
    AfterStop   []func() error

    // Other options for implementations of the interface
    // can be stored in a context
    Context context.Context

    Signal bool
}

// 创建newOptions构造函数,传入的是定义的函数类型 type Option func(*Options)  在micro.go里面
func newOptions(opts ...Option) Options {
    // 默认结构体属性值
    opt := Options{
        Auth:      auth.DefaultAuth,
        Broker:    broker.DefaultBroker,
        Cmd:       cmd.DefaultCmd,
        Config:    config.DefaultConfig,
        Client:    client.DefaultClient,
        Server:    server.DefaultServer,
        Store:     store.DefaultStore,
        Registry:  registry.DefaultRegistry,
        Router:    router.DefaultRouter,
        Runtime:   runtime.DefaultRuntime,
        Transport: transport.DefaultTransport,
        Context:   context.Background(),
        Signal:    true,
    }

    // 遍历,并对每一项应用返回的闭包到结构体的选项变量
    for _, o := range opts {
        o(&opt)
    }

    return opt
}

// 设置Broker
// Broker to be used for service
func Broker(b broker.Broker) Option {
    // 闭包,进入的是一个外部变量,返回的是一个匿名函数
    return func(o *Options) {
        o.Broker = b
        // Update Client and Server
        o.Client.Init(client.Broker(b))
        o.Server.Init(server.Broker(b))
    }
}

// 。。。。下面都是一样的道理

// 设置Cmd
func Cmd(c cmd.Cmd) Option {
    return func(o *Options) {
        o.Cmd = c
    }
}

// 设置Client。。。。。下面一大堆也是一样的道理
// Client to be used for service
func Client(c client.Client) Option {
    return func(o *Options) {
        o.Client = c
    }
}

// Context specifies a context for the service.
// Can be used to signal shutdown of the service and for extra option values.
func Context(ctx context.Context) Option {
    return func(o *Options) {
        o.Context = ctx
    }
}

// HandleSignal toggles automatic installation of the signal handler that
// traps TERM, INT, and QUIT.  Users of this feature to disable the signal
// handler, should control liveness of the service through the context.
func HandleSignal(b bool) Option {
    return func(o *Options) {
        o.Signal = b
    }
}

// Profile to be used for debug profile
func Profile(p profile.Profile) Option {
    return func(o *Options) {
        o.Profile = p
    }
}

// Server to be used for service
func Server(s server.Server) Option {
    return func(o *Options) {
        o.Server = s
    }
}

// Store sets the store to use
func Store(s store.Store) Option {
    return func(o *Options) {
        o.Store = s
    }
}

// Registry sets the registry for the service
// and the underlying components
func Registry(r registry.Registry) Option {
    return func(o *Options) {
        o.Registry = r
        // Update router
        o.Router.Init(router.Registry(r))
        // Update server
        o.Server.Init(server.Registry(r))
        // Update Broker
        o.Broker.Init(broker.Registry(r))
    }
}

// Tracer sets the tracer for the service
func Tracer(t trace.Tracer) Option {
    return func(o *Options) {
        o.Server.Init(server.Tracer(t))
    }
}

// Auth sets the auth for the service
func Auth(a auth.Auth) Option {
    return func(o *Options) {
        o.Auth = a
        o.Server.Init(server.Auth(a))
    }
}

// Config sets the config for the service
func Config(c config.Config) Option {
    return func(o *Options) {
        o.Config = c
    }
}

// Selector sets the selector for the service client
func Selector(s selector.Selector) Option {
    return func(o *Options) {
        o.Client.Init(client.Selector(s))
    }
}

// Transport sets the transport for the service
// and the underlying components
func Transport(t transport.Transport) Option {
    return func(o *Options) {
        o.Transport = t
        // Update Client and Server
        o.Client.Init(client.Transport(t))
        o.Server.Init(server.Transport(t))
    }
}

// Runtime sets the runtime
func Runtime(r runtime.Runtime) Option {
    return func(o *Options) {
        o.Runtime = r
    }
}

// Router sets the router
func Router(r router.Router) Option {
    return func(o *Options) {
        o.Router = r
        // Update client
        o.Client.Init(client.Router(r))
    }
}

// Convenience options

// Address sets the address of the server
func Address(addr string) Option {
    return func(o *Options) {
        o.Server.Init(server.Address(addr))
    }
}

// Name of the service
func Name(n string) Option {
    return func(o *Options) {
        o.Server.Init(server.Name(n))
    }
}

// Version of the service
func Version(v string) Option {
    return func(o *Options) {
        o.Server.Init(server.Version(v))
    }
}

// Metadata associated with the service
func Metadata(md map[string]string) Option {
    return func(o *Options) {
        o.Server.Init(server.Metadata(md))
    }
}

// Flags that can be passed to service
func Flags(flags ...cli.Flag) Option {
    return func(o *Options) {
        o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...)
    }
}

// Action can be used to parse user provided cli options
func Action(a func(*cli.Context) error) Option {
    return func(o *Options) {
        o.Cmd.App().Action = a
    }
}

// RegisterTTL specifies the TTL to use when registering the service
func RegisterTTL(t time.Duration) Option {
    return func(o *Options) {
        o.Server.Init(server.RegisterTTL(t))
    }
}

// RegisterInterval specifies the interval on which to re-register
func RegisterInterval(t time.Duration) Option {
    return func(o *Options) {
        o.Server.Init(server.RegisterInterval(t))
    }
}

// WrapClient is a convenience method for wrapping a Client with
// some middleware component. A list of wrappers can be provided.
// Wrappers are applied in reverse order so the last is executed first.
func WrapClient(w ...client.Wrapper) Option {
    return func(o *Options) {
        // apply in reverse
        for i := len(w); i > 0; i-- {
            o.Client = w[i-1](o.Client)
        }
    }
}

// WrapCall is a convenience method for wrapping a Client CallFunc
func WrapCall(w ...client.CallWrapper) Option {
    return func(o *Options) {
        o.Client.Init(client.WrapCall(w...))
    }
}

// WrapHandler adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w ...server.HandlerWrapper) Option {
    return func(o *Options) {
        var wrappers []server.Option

        for _, wrap := range w {
            wrappers = append(wrappers, server.WrapHandler(wrap))
        }

        // Init once
        o.Server.Init(wrappers...)
    }
}

// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
    return func(o *Options) {
        var wrappers []server.Option

        for _, wrap := range w {
            wrappers = append(wrappers, server.WrapSubscriber(wrap))
        }

        // Init once
        o.Server.Init(wrappers...)
    }
}

// Before and Afters

// BeforeStart run funcs before service starts
func BeforeStart(fn func() error) Option {
    return func(o *Options) {
        o.BeforeStart = append(o.BeforeStart, fn)
    }
}

// BeforeStop run funcs before service stops
func BeforeStop(fn func() error) Option {
    return func(o *Options) {
        o.BeforeStop = append(o.BeforeStop, fn)
    }
}

// AfterStart run funcs after service starts
func AfterStart(fn func() error) Option {
    return func(o *Options) {
        o.AfterStart = append(o.AfterStart, fn)
    }
}

// AfterStop run funcs after service stops
func AfterStop(fn func() error) Option {
    return func(o *Options) {
        o.AfterStop = append(o.AfterStop, fn)
    }
}
本文目录