Go语言设计模式之函数式选项模式

Go语言设计模式之函数式选项模式

本文主要介绍了Go语言中函数式选项模式及该设计模式在实际编程中的应用。

为什么需要函数式选项模式?

最近看go-micro/options.go源码的时候,发现了一段关于服务注册的代码如下:

type Options struct {
    Broker    broker.Broker
    Cmd       cmd.Cmd
    Client    client.Client
    Server    server.Server
    Registry  registry.Registry
    Transport transport.Transport

    // 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
}

func newOptions(opts ...Option) Options {
    opt := Options{
        Broker:    broker.DefaultBroker,
        Cmd:       cmd.DefaultCmd,
        Client:    client.DefaultClient,
        Server:    server.DefaultServer,
        Registry:  registry.DefaultRegistry,
        Transport: transport.DefaultTransport,
        Context:   context.Background(),
    }

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

    return opt
}

当时呢,也不是很明白newOptions这个构造函数为什么要这么写,但是后面在微信群里看到有人也再发类似的代码问为什么要这么写,后来在群里讨论的时候才知道了这是一种设计模式–函数式选项模式

可能大家看到现在也不是很明白我说的问题到底是什么,我把它简单提炼一下。

我们现在有一个结构体,定义如下:

type Option struct {
    A string
    B string
    C int
}

现在我们需要为其编写一个构造函数,我们可能会写成下面这种方式:

func newOption(a, b string, c int) *Option {
    return &Option{
        A: a,
        B: b,
        C: c,
    }
}

上面的代码很好理解,也是我们一直在写的。有什么问题吗?

我们现在来思考以下两个问题:

  1. 我们可能需要为Option的字段指定默认值
  2. Option的字段成员可能会发生变更

选项模式

我们先定义一个OptionFunc的函数类型

type OptionFunc func(*Option)

然后利用闭包为每个字段编写一个设置值的With函数:

func WithA(a string) OptionFunc {
    return func(o *Option) {
        o.A = a
    }
}

func WithB(b string) OptionFunc {
    return func(o *Option) {
        o.B = b
    }
}

func WithC(c int) OptionFunc {
    return func(o *Option) {
        o.C = c
    }
}

然后,我们定义一个默认的Option如下:

var (
    defaultOption = &Option{
        A: "A",
        B: "B",
        C: 100,
    }
)

最后编写我们新版的构造函数如下:

func newOption2(opts ...OptionFunc) (opt *Option) {
    opt = defaultOption
    for _, o := range opts {
        o(opt)
    }
    return
}

测试一下:

func main() {
    x := newOption("nazha", "小王子", 10)
    fmt.Println(x)
    x = newOption2()
    fmt.Println(x)
    x = newOption2(
        WithA("沙河娜扎"),
        WithC(250),
    )
    fmt.Println(x)
}

输出:

&{nazha 小王子 10}
&{A B 100}
&{沙河娜扎 B 250}

这样一个使用函数式选项设计模式的构造函数就实现了。这样默认值也有了,以后再要为Option添加新的字段也不会影响之前的代码。

推荐阅读:

Go 函数式选项模式

原文地址:https://www.cnblogs.com/Dr-wei/p/11742431.html

时间: 2024-11-07 00:50:24

Go语言设计模式之函数式选项模式的相关文章

golang 设计模式之选项模式

有时候一个函数会有很多参数,为了方便函数的使用,我们会给希望给一些参数设定默认值,调用时只需要传与默认值不同的参数即可,类似于 python 里面的默认参数和字典参数,虽然 golang 里面既没有默认参数也没有字典参数,但是我们有选项模式 可变长参数列表 在这之前,首先需要介绍一下可变长参数列表,顾名思义,就是参数的个数不固定,可以是一个也可以是多个,最典型的用法就是标准库里面的 fmt.Printf,语法比较简单,如下面例子实现任意多个参数的加法 func add(nums ...int)

设计模式之适配者模式——Java语言描述 | Amos H's blog

适配器模式是作为两个不兼容的接口之间的桥梁.这种类型的设计模糊属于结构性模式,它结合了两个独立接口的功能 概念阐述 使用适配器模式可以解决在软件系统中,将一些旧的类放入新环境中,但是新环境要求的接口旧的类不能满足的情况. 使用情形: 系统需要使用旧的类,但是此类的接口不符合系统的需要 需要建立一个可以重复使用的类,用于一些彼此之间没有太大关系的一些类 通过接口转换,将一个类插入到另一个类中 优点: 可以让任何两个没有关联的类一起运行 提高了类的复用 增加了类的透明度 灵活性好 缺点: 过多的使用

IOS设计模式之四(备忘录模式,命令模式)

本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq)翻译,如果你发现有什么错误,请与我联系谢谢. 备忘录(Memento)模式 备忘录模式快照对象的内部状态并将其保存到外部.换句话说,它将状态保存到某处,过会你可以不破坏封装的情况下恢复对象的状态,也就是说原来对象中的私有数据仍然是私有的. 如何使用备忘录模式 在ViewController.m中增加

设计模式之中介者模式(Mediator)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕该功能.如查找.排序等,一种经常使用的方法是硬编码(Hard Coding)在一个类中,如须要提供多种查找算法,能够将这些算法写到一个类中,在该类中提供多个方法,每个方法相应一个详细的查找算法:当然也能够将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件推断语句来进行选择.

《Java设计模式》之接口模式

-----------模式是思想的体现,而非具体的实现. 抽象的讲,类的接口是类允许其他类对象访问的方法与字段集.接口通常代表一种承诺,即方法需要实现接口方法名表示的操作,遵循代码注释和其他文档说明,类的实现就是方法体中的代码. java不允许多重继承,也就是说一个子类只能有一个父类,Son extends FatherA,FatherB 是错误的为了弥补这点不足,java允许实现多个接口, 接口就是给出一些没有内容的方法,类似于C++中的虚类.到具体用的时候再由用的方法自己定义内容,要注意的是

【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件判断语句来进行选择.这

设计模式学习之访问者模式

访问者模式,是行为型设计模式之一.访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了. 访问者模式的基本想法是,软件系统中拥有一个由许多对象构成的.比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接受访问者对象的访问.访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理.

设计模式3—行为型模式

行为型模式用来对类或对象怎样交互和怎样分配职责进行描述,主要包含以下11种设计模式: 1. 模板方法模式(Template Method Pattern)使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 2. 命令模式(Command Pattern)是将一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. 3. 责任链模式(Chain of Responsibility Pattern),在该模式里,很多对象由每一个