【golang】22、functional options | 函数式编程、闭包

文章目录

  • 一、配置 Option
    • 1.1 options
    • 1.2 funcitonal options

一、配置 Option

1.1 options

https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html

I’ve been trying on and off to find a nice way to deal with setting options in a Go package I am writing. Options on a type, that is. The package is intricate and there will probably end up being dozens of options. There are many ways to do this kind of thing, but I wanted one that felt nice to use, didn’t require too much API (or at least not too much for the user to absorb), and could grow as needed without bloat.

I’ve tried most of the obvious ways: option structs, lots of methods, variant constructors, and more, and found them all unsatisfactory. After a bunch of trial versions over the past year or so, and a lot of conversations with other Gophers making suggestions, I’ve finally found one I like. You might like it too. Or you might not, but either way it does show an interesting use of self-referential functions.

I hope I have your attention now.

Let’s start with a simple version. We’ll refine it to get to the final version.

First, we define an option type. It is a function that takes one argument, the Foo we are operating on.

type option func(*Foo)

The idea is that an option is implemented as a function we call to set the state of that option. That may seem odd, but there’s a method in the madness.

Given the option type, we next define an Option method on *Foo that applies the options it’s passed by calling them as functions. That method is defined in the same package, say pkg, in which Foo is defined.

This is Go, so we can make the method variadic and set lots of options in a given call:

// Option sets the options specified.
func (f *Foo) Option(opts ...option) {
    for _, opt := range opts {
        opt(f)
    }
}

Now to provide an option, we define in pkg a function with the appropriate name and signature. Let’s say we want to control verbosity by setting an integer value stored in a field of a Foo. We provide the verbosity option by writing a function with the obvious name and have it return an option, which means a closure; inside that closure we set the field:

// Verbosity sets Foo's verbosity level to v.
func Verbosity(v int) option {
    return func(f *Foo) {
        f.verbosity = v
    }
}

Why return a closure instead of just doing the setting? Because we don’t want the user to have to write the closure and we want the Option method to be nice to use. (Plus there’s more to come…)

In the client of the package, we can set this option on a Foo object by writing:

foo.Option(pkg.Verbosity(3))

That’s easy and probably good enough for most purposes, but for the package I’m writing, I want to be able to use the option mechanism to set temporary values, which means it would be nice if the Option method could return the previous state. That’s easy: just save it in an empty interface value that is returned by the Option method and the underlying function type. That value flows through the code:

type option func(*Foo) interface{}

// Verbosity sets Foo's verbosity level to v.
func Verbosity(v int) option {
    return func(f *Foo) interface{} {
        previous := f.verbosity
        f.verbosity = v
        return previous
    }
}

// Option sets the options specified.
// It returns the previous value of the last argument.
func (f *Foo) Option(opts ...option) (previous interface{}) {
    for _, opt := range opts {
        previous = opt(f)
    }
    return previous
}

The client can use this the same as before, but if the client also wants to restore a previous value, all that’s needed is to save the return value from the first call, and then restore it.

prevVerbosity := foo.Option(pkg.Verbosity(3))
foo.DoSomeDebugging()
foo.Option(pkg.Verbosity(prevVerbosity.(int)))

The type assertion in the restoring call to Option is clumsy. We can do better if we push a little harder on our design.

First, redefine an option to be a function that sets a value and returns another option to restore the previous value.

type option func(f *Foo) option


This self-referential function definition is reminiscent of a state machine. Here we’re using it a little differently: it’s a function that returns its inverse.

Then change the return type (and meaning) of the Option method of *Foo to option from interface{}:

// Option sets the options specified.
// It returns an option to restore the last arg's previous value.
func (f *Foo) Option(opts ...option) (previous option) {
    for _, opt := range opts {
        previous = opt(f)
    }
    return previous
}

The final piece is the implementation of the actual option functions. Their inner closure must now return an option, not an interface value, and that means it must return a closure to undo itself. But that’s easy: it can just recur to prepare the closure to undo the original! It looks like this:

// Verbosity sets Foo's verbosity level to v.
func Verbosity(v int) option {
    return func(f *Foo) option {
        previous := f.verbosity
        f.verbosity = v
        return Verbosity(previous)
    }
}

Note the last line of the inner closure changed from
return previous
to
return Verbosity(previous)
Instead of just returning the old value, it now calls the surrounding function (Verbosity) to create the undo closure, and returns that closure.

Now from the client’s view this is all very nice:

prevVerbosity := foo.Option(pkg.Verbosity(3))
foo.DoSomeDebugging()
foo.Option(prevVerbosity)

And finally we take it up one more level, using Go’s defer mechanism to tidy it all up in the client:

func DoSomethingVerbosely(foo *Foo, verbosity int) {
    // Could combine the next two lines,
    // with some loss of readability.
    prev := foo.Option(pkg.Verbosity(verbosity))
    defer foo.Option(prev)
    // ... do some stuff with foo under high verbosity.
}

It’s worth noting that since the “verbosity” returned is now a closure, not a verbosity value, the actual previous value is hidden. If you want that value you need a little more magic, but there’s enough magic for now.

The implementation of all this may seem like overkill but it’s actually just a few lines for each option, and has great generality. Most important, it’s really nice to use from the point of view of the package’s client. I’m finally happy with the design. I’m also happy at the way this uses Go’s closures to achieve its goals with grace.

1.2 funcitonal options

https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/360234.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flink中StateBackend(工作状态)与Checkpoint(状态快照)的关系

State Backends 由 Flink 管理的 keyed state 是一种分片的键/值存储,每个 keyed state 的工作副本都保存在负责该键的 taskmanager 本地中。另外,Operator state 也保存在机器节点本地。Flink 定期获取所有状态的快照,并将这些快照复制到持…

Python面向对象编程:探索代码的结构之美

文章目录 一、引言二、为什么学习面向对象编程2.1 提高代码的可维护性:通过封装、继承和多态实现模块化设计2.2 提升代码的复用性:通过类和对象的创建实现代码的重用 三、类和对象的基本概念3.1 类和对象的定义和关系:类是对象的模板&#xf…

通过Nacos权重配置,实现微服务金丝雀发布效果(不停机部署)

在微服务项目迭代的过程中,不可避免需要上线;上线对应着部署,或者升级部署;部署对应着修改,修改则意味着风险。 传统的部署都需要先停止旧系统,然后部署新系统,之后需要对新系统进行全面的功能测试&#xf…

力扣hot100 n皇后 满注释版 通俗易懂

Problem: 51. N 皇后 文章目录 思路Code 思路 &#x1f468;‍&#x1f3eb; 参考地址 考虑每一行哪个位置放皇后判断是否合法递归下一行 Code class Solution {int n;char[][] board;List<List<String>> res new ArrayList<>();public List<List&l…

python_蓝桥杯刷题记录_笔记_入门2

前言 现在正式进入蓝桥杯的刷题啦&#xff0c;用python来做算法题&#xff0c;因为我之前其实都是用C来做题的&#xff0c;但是今年的话我打算换python来试试&#xff0c;很明显因为也才这学期接触python 加上之前C做题也比较菜&#xff0c;所以我打算用python重新来做题&#…

常用抓包软件集合(Fiddler、Charles)

1. Fiddler 介绍&#xff1a;Fiddler是一个免费的HTTP和HTTPS调试工具&#xff0c;支持Windows平台。它可以捕获HTTP和HTTPS流量&#xff0c;并提供了丰富的调试和分析功能。优点&#xff1a;易于安装、易于使用、支持多种扩展、可以提高开发效率。缺点&#xff1a;只支持Wind…

向上调整向下调整算法

目录 AdjustUp向上调整 AdjustDown向下调整 AdjustUp向上调整 前提是&#xff1a;插入数据之后&#xff0c;除去插入的数据其他的数据还是为堆 应用&#xff1a;插入数据。 先插入一个10到数组的尾上&#xff0c;再进行向上调整算法&#xff0c;直到满足堆。 性质&#xff1…

SD卡写保护无法格式化怎么办?

一般来说&#xff0c;写保护&#xff08;也称为只读&#xff09;是数据存储设备防止写入新数据或修改旧信息的能力。换句话说&#xff0c;您可以读取存储在磁盘上的信息&#xff0c;但是却不能删除、更改或复制它们&#xff0c;因为访问会被拒绝。那么SD卡有写保护怎么格式化呢…

Vue(十九):ElementUI 扩展实现树形结构表格组件的勾父选子、半勾选、过滤出半勾选节点功能

效果 原理分析 从后端获取数据后,判断当前节点是否勾选,从而判断是否勾选子节点勾选当前节点时,子节点均勾选全勾选与半勾选与不勾选的样式处理全勾选和全取消勾选的逻辑筛选出半勾选的节点定义变量 import {computed, nextTick, reactive, ref} from vue; import {tree} f…

解决打开页面显示源代码和乱码

用系统记事本打开 点击文件》另存为 选择编码&#xff1a;ANSI 保存》要替换它吗?》是 重新打开页面&#xff0c;显示正常&#xff0c;解决问题。

D2025——双通道音频功率放大电路,外接元件少, 通道分离性好,3V 的低压下可正常使用

D2025 为立体声音频功率放大集成电路&#xff0c;适用于各类袖珍或便携式立体声 收录机中作功率放放大器。 D2025 采用 DIP16 封装形式。 主要特点&#xff1a;  适用于立体声或 BTL 工作模式  外接元件少  通道分离性好  电源电压范围宽&#xff08;3V~12V…

Jenkins自动化打包

Jenkins自动化打包 下载安装 我们直接从官网https://www.jenkins.io/download/ 下载所需的Jenkins文件 如上图所示, 选择Windows版本,下面就是一路安装即可,需要注意的是,选择作为系统服务选项, 不要自己设置账号密码登录. Web配置 安装完根据提示在浏览器打开 http://lo…

01、全文检索 ------ 反向索引库 与 Lucene 的介绍

目录 全文检索 ------ 反向索引库 与 LuceneSQL模糊查询的问题反向索引库反向索引库的查询 Lucene&#xff08;全文检索技术&#xff09;Lucene能做什么Lucene存在的问题Solr 和 Elasticsearch 与 Lucene 的关系 全文检索 ------ 反向索引库 与 Lucene MySQL一些索引词汇解释 …

故障诊断 | 一文解决,BP神经网络的故障诊断(Matlab)

文章目录 效果一览文章概述专栏介绍模型描述源码设计参考资料效果一览 文章概述 故障诊断 | 一文解决,BP神经网络的故障诊断(Matlab) 专栏介绍 订阅【故障诊断】专栏,不定期更新机器学习和深度学习在故障诊断中的应用;订阅

52个值得收藏的无代码AI平台【2024】

“无代码”不仅仅是炒作。 这是一场革命。 在无代码之前&#xff0c;如果你想制作一个网站&#xff0c;你需要一名技术网络开发人员。 现在&#xff0c;你可以使用 Bubble、Webflow、Carrd 或无数其他可视化工具。 人工智能领域也发生了同样的情况。 在无代码之前&#xff0c;你…

国网四川宜宾供电公司:基于“RPA+AI”融合技术的电网设备隐患缺陷智能化识别应用

推荐单位&#xff1a;国网四川省电力公司宜宾供电公司 本文作者&#xff1a;杨鑫、唐龙、钟睿、李小航、孙雪冬 摘 要&#xff1a;为推进电力企业生产业务数字化转型&#xff0c;提高基层班组数字化运维水平。本文通过一线班组对变电站视频巡视、设备故障判断应用场景需求分析…

搭建幻兽帕鲁需要什么样的服务器

作为一个开放世界生存制造类游戏《幻兽帕鲁》收获了空前绝后的热度&#xff0c;玩家们在游戏中通过在地图上捕捉收集到的“帕鲁”进行训练&#xff0c;合理利用他们的能力进行战斗&#xff0c;建立自己的家园、开辟新的世界、解锁新的冒险情节&#xff0c;获取更多游戏信息增加…

langchain+xray:prompt控制漏洞扫描

写在前面 xray是长亭推出的一款漏洞扫描工具。 langchain是调用LLM大模型完成自动化任务的框架。 本篇文章是对langchain自定义工具的探索&#xff0c;通过编写一个xray调用的工具&#xff0c;联合ChatGPT对xray进行调用&#xff0c;实现对目标的漏洞扫描。 xray功能分析 …

悄悄话 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 给定一个二叉树&#xff0c;每个节点上站一个人&#xff0c;节点数字表示父节点到该节点传递悄悄话需要花费的时间。 初始时&#xff0c;根节点所在位置的人有一…

vue3.0 + 动态加载组件 + 全局注册组件

首先 vue 动态加载组件使用的是 component 标签&#xff0c;并通过设置组件的is 属性来指定要渲染的组件。例如&#xff1a; <component :is"currentComponent"></component>其中&#xff0c;currentComponent 是一个变量&#xff0c;它的值可以是以下几…