当前位置: 首页 > news >正文

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

目录

  • 引入
  • 函数选项模式(functional options pattern)
    • 可选参数
    • 默认值
    • 接口类型版本

引入

假设现在需要定义一个包含多个配置项的结构体,具体定义如下:

// DoSomethingOption 定义配置项
type DoSomethingOption struct {// a 配置aa string// b 配置bb string// c 配置cc string// ...
}

这个配置结构体中的字段可能有几个也可能有十几个,

现在写一个构造函数(初始化函数):

func NewDoSomethingOption(a,b,c string) *DoSomethingOption {return &DoSomethingOption{a: a,b: b,c: c,}
}

产生两个问题:

  1. 如果 DoSomethingOption 有十几个字段,构造函数需要定义十几个参数吗?如何为某些配置项指定默认值?
  2. DoSomethingOption 随着业务发展不断新增字段后,构造函数是否也需要同步变更?变更了构造函数是否又会影响已有代码?

函数选项模式(functional options pattern)

函数选项模式(Functional Options Pattern)也称为选项模式(Options Pattern),是一种创造性的设计模式,允许使用接受零个或多个函数作为参数的可变构造函数构建复杂结构。

可选参数

Go语言中的函数不支持默认参数,但又原生支持可变长参数。

可变长参数的具体类型则需要好好设计一下。它必须满足以下条件:

  1. 不同的函数参数拥有相同的类型
  2. 指定函数参数能为特定的配置项赋值
  3. 支持扩展新的配置项

先定义一个名为OptionFunc的类型,它实际上一个接收 *DoSomethingOption 作为参数并且会在函数内部修改其字段的函数。

type OptionFunc func(*DoSomethingOption)

接下来,我们为DoSomethingOption字段编写一系列WithXxx函数,其返回值是一个修改指定字段的闭包函数。

// WithB 将 DoSomethingOption 的 b 字段设置为指定值
func WithB(b string) OptionFunc {return func(o *DoSomethingOption) {o.b = b}
}// WithC 将 DoSomethingOption 的 b 字段设置为指定值
func WithC(c string) OptionFunc {return func(o *DoSomethingOption) {o.c = c}
}

WithXxx是函数选项模式中约定成俗的函数名称格式,这样构造函数就可以改写成如下方式了,除了必须传递a参数外,其他的参数都是可选的。

func NewDoSomethingOption(a string, opts ...OptionFunc) *DoSomethingOption {o := &DoSomethingOption{a: a}for _, opt := range opts {opt(o)}return o
}

只想传入ab参数时,可以按如下方式:

NewDoSomethingOption("德玛西亚", WithB(10))

默认值

在使用函数选项模式后,可以很方便的为某些字段设置默认值,例如下面的示例代码中B默认值为100。

const defaultValueB = 100func NewDoSomethingOption(a string, opts ...OptionFunc) *DoSomethingOption {o := &DoSomethingOption{a: a, b: defaultValueB}  // 字段b使用默认值for _, opt := range opts {opt(o)}return o
}

以后要为DoSomethingOption添加新的字段时也不会影响之前的代码,只需要为新字段编写对应的With函数即可。

接口类型版本

在一些场景下,并不想对外暴露具体的配置结构体,而是仅仅对外提供一个功能函数,这时可以将对应的结构体定义为小写字母开头,将其限制只在包内部使用。

// doSomethingOption 定义一个内部使用的配置项结构体
type doSomethingOption struct {a stringb intc bool// ...
}

此时,同样是使用函数选项模式,但可以通过使用接口类型来“隐藏”内部的逻辑。

// IOption 定义一个接口类型
type IOption interface {apply(*doSomethingOption)
}// funcOption 定义funcOption类型,实现 IOption 接口
type funcOption struct {f func(*doSomethingOption)
}func (fo funcOption) apply(o *doSomethingOption) {fo.f(o)
}func newFuncOption(f func(*doSomethingOption)) IOption {return &funcOption{f: f,}
}// WithB 将b字段设置为指定值的函数
func WithB(b int) IOption {return newFuncOption(func(o *doSomethingOption) {o.b = b})
}// DoSomething 包对外提供的函数
func DoSomething(a string, opts ...IOption) {o := &doSomethingOption{a: a}for _, opt := range opts {opt.apply(o)}// 在包内部基于o实现逻辑...fmt.Printf("o:%#v\n", o)
}

如此一来,我们只需对外提供一个DoSomething的功能函数和一系列WithXxx函数。对于调用方来说,使用起来也很方便。

DoSomething("q1mi")
DoSomething("q1mi", WithB(100))

相关文章:

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

目录引入函数选项模式(functional options pattern)可选参数默认值接口类型版本引入 假设现在需要定义一个包含多个配置项的结构体,具体定义如下: // DoSomethingOption 定义配置项 type DoSomethingOption struct {// a 配置aa…...

ClickHouse 数据类型、函数大小写敏感性

这里写自定义目录标题SELECT *FROM system.data_type_families注意:case_insensitive0 表示大小写敏感。 ClickHouse 的 String 类型、Int 类型、Float 类型、Decimal类型等都是大小写敏感的(case_sensitive0)。关于ClickHouse大小写敏感&am…...

nodejs基于vue 网上商城购物系统

可定制框架:ssm/Springboot/vue/python/PHP/小程序/安卓均可开发 目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2. 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行…...

掌握MySQL分库分表(一)数据库性能优化思路、分库分表优缺点

文章目录MySQL数据库性能优化思路【面试题】不分库分表软优化硬优化分库分表结论分库分表能解决的问题解决数据库本身瓶颈连接数解决系统本身IO、CPU瓶颈分库分表带来的问题问题⼀ 跨节点数据库Join关联查询问题二 分库操作带来的分布式事务问题问题三 执行的SQL排序、翻页、函…...

何为小亚细亚?

一、小亚细亚安纳托利亚(Anatolia),又名小亚细亚或西亚美尼亚,是亚洲西南部的一个半岛,隶属于土耳其。安纳托利亚半岛,北临黑海,西临爱琴海,南濒地中海,东接亚美尼亚高原…...

【mircopython】ESP32配置与烧录版本

下载ESP32的Micropython固件 官方连接https://www.micropython.org/download/esp32/ 看了下描述,上面的是IDF4.x系列编译,下面是IDF3.x系列编译,我们默认选新的 下载安装CP2102驱动 CP210x USB to UART Bridge VCP Drivers - Silicon Labs…...

Yaml:通过extrac进行传参,关联---接口关联封装(基于一个独立YAML的文件)

一:在common包中,封装一个yaml_util的工具包 1. 将获取到的数据,写入到extrac.yaml文件中,通过data def write_extract_yaml(data): 2. 需要用到该参数时,读取extrac.yaml文件中,由于会有多个参数&#x…...

vue - vue中对Vant日历组件(calendar)的二次封装

vue中对vant日历选择器组件实现的的二次封装;主要实现功能如下: 主要功能: 日期区间选择(基本);自定义选择器的底部按钮,添加清除时间操作(slot插槽);指定默认选中的日期…...

详解C++的类型转换

文章目录前言一、C语言中的类型转换二、为什么C需要四种转换三、C强制类型转换3.1 static_cast3.2 reinterpret_cast3.3 const_cast3.4 dynamic_cast四、RTTI总结前言 在C语言的类型转换有一个非常大的坑,有好多悄悄地转换,有时候把我们转换的就蒙了,因为C要兼容C语言,所以C就…...

NLP文本自动生成介绍及Char-RNN中文文本自动生成训练demo

前言 文本自动生成是自然语言处理领域的一个重要研究方向,实现文本自动生成也是人工智能走向成熟的一个重要标志。文本自动生成技术极具应用前景。 例如,文本自动生成技术可以应用于智能问答与对话、机器翻译等系统,实现更加智能和自然的人机…...

Teradata 离场,企业数据分析平台如何应对变革?

近日大数据分析和数仓软件巨头 Teradata(TD)宣布基于中国商业环境的评估,退出在中国的直接运营。TD 是全球最大的专注于大数据分析、数仓和整合营销管理解决方案的供应商之一,其早在 1997 年就进入中国,巅峰期占据半数…...

QWebEngineView-官翻

文章目录特性公共成员函数重实现公共成员函数公有槽函数信号静态公有成员函数保护成员函数重实现保护成员函数额外继承成员详细描述特性文档编制成员函数文档QWebEngineView::**QWebEngineView**([QWidget](../../W/QWidget.md) **parent* Q_NULLPTR)[virtual] QWebEngineView…...

网络安全高级攻击

对分类器的高层次攻击可以分为以下三种类型:对抗性输入:这是专门设计的输入,旨在确保被误分类,以躲避检测。对抗性输入包含专门用来躲避防病毒程序的恶意文档和试图逃避垃圾邮件过滤器的电子邮件。数据中毒攻击:这涉及…...

优思学院:六西格玛中的水平对比方法是什么?

水平对比,就是比较不同事物之间的差异。 这个概念在六西格玛管理中也很重要,也就是我们经常说的标杆管理,经常被用来寻找行业中最好的做法,以帮助组织改进自身的绩效。 在六西格玛管理中,水平对比有三种常见的应用方式…...

UVa 690 Pipeline Scheduling 流水线调度 二进制表示状态 DFS 剪枝

题目链接:Pipeline Scheduling 题目描述: 给定一张5n(1≤n≤20)5\times n(1\le n\le20)5n(1≤n≤20)的资源需求表,第iii行第jjj列的值为’X’表示进程在jjj时刻需要使用使用资源iii,如果为’.则表示不需要使用。你的任务是安排十个…...

【ArcGIS Pro二次开发】(6):工程(Project)的基本操作

在ArcGIS Pro中我们对工程的基本操作一般包括打开、新建、保存等。下面演示在二次开发中如何用代码进行以上操作。 新建一个项目,命名为【ProjectManager】,添加8个按钮,命名为【CreateEmptyProject、CreateProjectByDefault、OpenExProjest…...

Qt OpenGL(四十)——Qt OpenGL 核心模式-雷达扫描效果

提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(四十)——Qt OpenGL 核心模式-雷达扫描效果 一、场景 上一篇文章介绍了在雷达坐标系中绘制飞行的飞机,其实雷达坐标系应该还有一个效果,就是扫描的效…...

群智能优化算法求解标准测试函数F1~F23之种群动态分布图(视频)

群智能优化算法求解标准测试函数F1的种群动态分布图群智能优化算法求解标准测试函数F2的种群动态分布图群智能优化算法求解标准测试函数F3的种群动态分布图群智能优化算法求解标准测试函数F4的种群动态分布图群智能优化算法求解标准测试函数F5的种群动态分布图群智能优化算法求…...

vue-axios封装与使用

一、简介 Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 这是一个使用率很高的前端网络请求库,几乎所有的前端项目都会使用,本文主要介绍的是如何在vue项目中使用axios,并对其进行全面的封装。 注意&#x…...

重要节点排序方法

文章目录研究背景提前约定基于节点近邻的排序方法度中心性(degree centrality, DC)半局部中心性(semilocal centrality, SLC)k-壳分解法基于路径排序的方法离心中心性 (Eccentricity, ECC)接近中心性 (closeness centrality, CC)K…...

附加模块--Qt OpenGL模块功能及架构

一、模块功能: 主要变化 Qt OpenGL 模块的分离: 在 Qt 6 中,原来的 Qt OpenGL 功能被拆分为多个模块 传统的 Qt OpenGL 模块 (QGL*) 已被标记为废弃 新的图形架构: Qt 6 引入了基于 QRhi (Qt Rendering Hardware Interface) 的…...

Linux 系统中的算法技巧与性能优化

引言​ Linux 系统以其开源、稳定和高度可定制的特性,在服务器端、嵌入式设备以及开发环境中得到了极为广泛的应用。对于开发者而言,不仅要掌握在 Linux 环境下实现各类算法的方法,更要知晓如何利用系统特性对算法进行优化,以提升…...

K8S认证|CKS题库+答案| 7. Dockerfile 检测

目录 7. Dockerfile 检测 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、修改 Dockerfile 3)、 修改 deployment.yaml 7. Dockerfile 检测 免费获取并激活 CKA_v1.31_模拟系统 题目 您必须在以…...

前后端分离开发 和 前端工程化

来源:黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)_哔哩哔哩_bilibili 前后端混合开发: 需要使用前端的技术栈开发前端的功能,又需要使用Java的技术栈…...

华为OD机考-内存冷热标记-多条件排序

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextInt();int[] arr new int[a];for(int…...

Markdown基础(1.2w字)

1. Markdown基础 这次就没目录了,因为md格式太乱了写示例,展示那些都太乱了,导致目录很乱。 (我是XX,出现了很多错误,有错误和我说) 1.1 Markdown简介 Markdown是一种轻量级的标记语言&#…...

链表题解——环形链表【LeetCode】

141. 环形链表 方法一 核心思想: 使用一个集合 seen 来记录已经访问过的节点。遍历链表,如果当前节点已经存在于集合中,说明链表存在环;否则,将当前节点添加到集合中,继续遍历。如果遍历结束(h…...

IT学习方法与资料分享

一、编程语言与核心技能:构建技术地基 1. 入门首选:Python 与 JavaScript Python:作为 AI 与数据科学的基石,可快速构建数据分析与自动化脚本开发能力。 JavaScript:Web 开发的核心语言,可系统掌握 React/V…...

网络编程之服务器模型与UDP编程

一、服务器模型 在网络通信中,通常要求一个服务器连接多个客户端 为了处理多个客户端的请求,通常有多种表现形式 1、循环服务器模型 一个服务器可以连接多个客户端,但同一时间只能连接并处理一个客户的请求 socket() 结构体 bind() listen() …...

智慧城市建设方案

第1章 总体说明 1.1 建设背景 1.2 建设目标 1.3 项目建设主要内容 1.4 设计原则 第2章 对项目的理解 2.1 现状分析 2.2 业务需求分析 2.3 功能需求分析 第3章 大数据平台建设方案 3.1 大数据平台总体设计 3.2 大数据平台功能设计 3.3 平台应用 第4章 政策标准保障…...