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

【golang/go语言】Go语言代码实践——高复用、易扩展性代码训练

某个项目里有一段老代码写的不是很好,想着能否通过自己掌握的知识,将其改善一下。感兴趣的小伙伴可以通过了解背景和需求,自己试想下该如何实现,如果有更好的方案也欢迎留言讨论。

1. 背景及需求

(1) 背景

假设我们的下游提供了一个定时任务接口CronJob(cron string, fun func()),它的入参有两个,分别是定时任务执行的频率,以及具体执行哪个函数。我们要借助该定时任务接口实现一个功能:对数据库中同一张表的所有数据的两个不同的字段完成赋值。为了给每个字段赋值,会分别设置两个定时任务,一个是正常的任务处理,5分钟执行一次,负责捞取最近30分钟的数据并完成赋值。另一个是补偿的任务处理,1小时执行一次,负责捞取最近24小时的数据并完成赋值。

(2) 需求

代码的实现要满足以下要求:

  • 因为都是捞取同一张表的数据并完成赋值,所以该部分代码应该复用同一份代码,防止出错;
  • 后续可能有新增字段需要类似的赋值逻辑,所以代码应该有良好的可扩展性;
  • 由于只有字段赋值逻辑不同,所以每次新增字段赋值需求时,代码的改动应该尽可能小。

2. 需求分析及实现

(1) 定时任务代码

由于我们依赖下游的定时任务接口,所以首先给出模拟的定时任务接口。

// CronJob 模拟定时任务,参数分别为调度时间和调度内容
func CronJob(cron string, fun func()) {fmt.Println("调度时间: ", cron)fmt.Println("定时任务执行开始。。。")fun()fmt.Println("定时任务执行结束。。。\n\n")
}

(2) 定义接口

我们需要实现的是对字段的赋值方法,由于同一字段的赋值,分为了正常的任务处理和补偿的任务处理两个,所以我们可以定义两个接口方法CommonFix

// ProcessJob 处理任务接口,定义了两个方法
type ProcessJob interface {Common(name string) func() // 正常的任务处理Fix(name string) func()    // 补偿的任务处理
}

(3) 接口的实现类

定义了接口后,接口的实现类应该分别实现上述两个方法,完成两个字段的赋值逻辑。而从数据库捞取数据的部分显然是通用的,该部分可以独立成为一个函数,该函数可以捞取数据,并将数据的主键id传参给实现类的两个方法,完成该条数据的赋值。

1) 通用部分

正常和补偿任务处理中通用的部分:

// Common 正常的任务处理中通用的部分
func Common(name string, fun func(params string)) func() {id := "$模拟id$"return func() {fmt.Println("任务名称为:", name)fmt.Println("正常任务处理的前置操作。。。")fun(id)fmt.Println("正常任务处理的后置操作。。。")}
}// Fix 补偿的任务处理中通用的部分
func Fix(name string, fun func(params string)) func() {id := "$模拟id$"return func() {fmt.Println("任务名称为:", name)fmt.Println("补偿任务处理的前置操作。。。")fun(id)fmt.Println("补偿任务处理的后置操作。。。")}
}

注意下通用方法的入参,这里分别用来接收实现类方法传入的参数,以及字段赋值逻辑的函数。

2) 第一个接口实现类

第一个处理任务,为第一个字段赋值:

// ProcessJob1 第一个处理任务,为第一个字段赋值
type ProcessJob1 struct{}// Common 第一个处理任务的正常任务处理
func (pj ProcessJob1) Common(name string) func() {return Common(name, func(id string) {fmt.Printf("常规任务处理中,为id为 %v 的第一个字段进行赋值", id)})
}// Fix 第一个处理任务的补偿任务处理
func (pj ProcessJob1) Fix(name string) func() {return Fix(name, func(id string) {fmt.Printf("补偿任务处理中,为id为 %v 的第一个字段进行赋值", id)})
}

这里需要注意下,由于定时任务接口只接受类型为func()的函数,所以接口实现类的CommonFix方法的返回值都是func()类型。而字段赋值逻辑函数的入参为从通用函数那传入的参数,即数据的主键id。

3) 第二个接口实现类

第二个处理任务,为第二个字段赋值:

// ProcessJob2 第二个处理任务,为第二个字段赋值
type ProcessJob2 struct{}// Common 第二个处理任务的正常任务处理
func (pj ProcessJob2) Common(name string) func() {return Common(name, func(id string) {fmt.Printf("常规任务处理中,为id为 %v 的第二个字段进行赋值", id)})
}// Fix 第二个处理任务的补偿任务处理
func (pj ProcessJob2) Fix(name string) func() {return Fix(name, func(id string) {fmt.Printf("补偿任务处理中,为id为 %v 的第二个字段进行赋值", id)})
}

(4) 实现类再封装

由于ProcessJob1和ProcessJob2都可以看做是ProcessJob类型,所以可以为实现类再封装一层,以便统一进行处理。通过分析需求可以知道,实现类的属性可以有任务名称、正常任务处理的调度时间、补偿任务处理的调度时间等组成,所以可以定义以下类型:

// JobConf 定时任务配置
type JobConf struct {Name       string     // 定时任务的名称CommonCron string     // 正常任务处理的调度时间FixCron    string     // 补偿任务处理的调度时间ProcessJob ProcessJob // 处理任务对象
}

(5) 添加/获取任务配置

定义了统一的类型之后,就可以创建一个该类型的列表,如果有新增的字段赋值需求,那么就可以很方便的进行扩展了,只需要在列表中新增一个任务配置即可。

// GetJobConfMap 获取所有的任务配置信息
func GetJobConfMap() map[string]JobConf {// 定义一个任务配置的列表,如有新增的任务,直接append一个新任务即可jobConfList := make([]JobConf, 0)jobConfList = append(jobConfList, JobConf{Name:       "job1",CommonCron: "1 * * * * *",FixCron:    "11 * * * * *",ProcessJob: ProcessJob1{},})jobConfList = append(jobConfList, JobConf{Name:       "job2",CommonCron: "2 * * * * *",FixCron:    "22 * * * * *",ProcessJob: ProcessJob2{},})// 获取任务名到任务配置的mapjobConfMap := make(map[string]JobConf)for _, jobConf := range jobConfList {jobConfMap[jobConf.Name] = jobConf}return jobConfMap
}

(6) 主函数

最后就是根据每个任务的配置,启动对应的定时任务了。

func main() {// 获取所有的任务配置信息jobConfMap := GetJobConfMap()// 为每个任务独立设置调度时间,以及处理内容for jobName, jobConf := range jobConfMap {processJob := jobConf.ProcessJobCronJob(jobConf.CommonCron, processJob.Common(jobName))CronJob(jobConf.FixCron, processJob.Fix(jobName))}
}

3. 完整代码

package mainimport ("fmt"
)// CronJob 模拟定时任务,参数分别为调度时间和调度内容
func CronJob(cron string, fun func()) {fmt.Println("调度时间: ", cron)fmt.Println("定时任务执行开始。。。")fun()fmt.Println("定时任务执行结束。。。\n\n")
}// ProcessJob 处理任务接口,定义了两个方法
type ProcessJob interface {Common(name string) func() // 正常的任务处理Fix(name string) func()    // 补偿的任务处理
}// Common 正常的任务处理中通用的部分
func Common(name string, fun func(params string)) func() {id := "$模拟id$"return func() {fmt.Println("任务名称为:", name)fmt.Println("正常任务处理的前置操作。。。")fun(id)fmt.Println("正常任务处理的后置操作。。。")}
}// Fix 补偿的任务处理中通用的部分
func Fix(name string, fun func(params string)) func() {id := "$模拟id$"return func() {fmt.Println("任务名称为:", name)fmt.Println("补偿任务处理的前置操作。。。")fun(id)fmt.Println("补偿任务处理的后置操作。。。")}
}// JobConf 定时任务配置
type JobConf struct {Name       string     // 定时任务的名称CommonCron string     // 正常任务处理的调度时间FixCron    string     // 补偿任务处理的调度时间ProcessJob ProcessJob // 处理任务对象
}// ProcessJob1 第一个处理任务,为第一个字段赋值
type ProcessJob1 struct{}// Common 第一个处理任务的正常任务处理
func (pj ProcessJob1) Common(name string) func() {return Common(name, func(id string) {fmt.Printf("常规任务处理中,为id为 %v 的第一个字段进行赋值", id)})
}// Fix 第一个处理任务的补偿任务处理
func (pj ProcessJob1) Fix(name string) func() {return Fix(name, func(id string) {fmt.Printf("补偿任务处理中,为id为 %v 的第一个字段进行赋值", id)})
}// ProcessJob2 第二个处理任务,为第二个字段赋值
type ProcessJob2 struct{}// Common 第二个处理任务的正常任务处理
func (pj ProcessJob2) Common(name string) func() {return Common(name, func(id string) {fmt.Printf("常规任务处理中,为id为 %v 的第二个字段进行赋值", id)})
}// Fix 第二个处理任务的补偿任务处理
func (pj ProcessJob2) Fix(name string) func() {return Fix(name, func(id string) {fmt.Printf("补偿任务处理中,为id为 %v 的第二个字段进行赋值", id)})
}// GetJobConfMap 获取所有的任务配置信息
func GetJobConfMap() map[string]JobConf {// 定义一个任务配置的列表,如有新增的任务,直接append一个新任务即可jobConfList := make([]JobConf, 0)jobConfList = append(jobConfList, JobConf{Name:       "job1",CommonCron: "1 * * * * *",FixCron:    "11 * * * * *",ProcessJob: ProcessJob1{},})jobConfList = append(jobConfList, JobConf{Name:       "job2",CommonCron: "2 * * * * *",FixCron:    "22 * * * * *",ProcessJob: ProcessJob2{},})// 获取任务名到任务配置的mapjobConfMap := make(map[string]JobConf)for _, jobConf := range jobConfList {jobConfMap[jobConf.Name] = jobConf}return jobConfMap
}func main() {// 获取所有的任务配置信息jobConfMap := GetJobConfMap()// 为每个任务独立设置调度时间,以及处理内容for jobName, jobConf := range jobConfMap {processJob := jobConf.ProcessJobCronJob(jobConf.CommonCron, processJob.Common(jobName))CronJob(jobConf.FixCron, processJob.Fix(jobName))}
}

相关文章:

【golang/go语言】Go语言代码实践——高复用、易扩展性代码训练

某个项目里有一段老代码写的不是很好,想着能否通过自己掌握的知识,将其改善一下。感兴趣的小伙伴可以通过了解背景和需求,自己试想下该如何实现,如果有更好的方案也欢迎留言讨论。 1. 背景及需求 (1) 背景 假设我们的下游提供了…...

[数据结构与算法(严蔚敏 C语言第二版)]第1章 绪论(学习复习笔记)

1.1 数据结构的研究内容 计算机解决问题的步骤 从具体问题抽象出数学模型设计一个解此数学模型的算法编写程序,进行测试、调试,直到解决问题 计算机解决问题的过程中寻求数学模型的实质是 分析问题,从中提取操作的对象,并找出这些…...

05_Pulsar的主要组件介绍与命令使用、名称空间、Pulsar的topic相关操作、Pulsar Topic(主题)相关操作_高级操作、

1.5.Apache Pulsar的主要组件介绍与命令使用 1.5.1.多租户模式 1.5.1.1. 什么是多租户 1.5.1.2.Pulsar多租户的相关特征_安全性(认证和授权) 1.5.1.3.Pulsar多租户的相关特性_隔离性 1.5.1.4.Pulsar多租户的相关操作 1-获取租户列表 2-创建租户 3-获取配…...

我的终端怎么莫名卡死了?shell下ctrl+s的含义

在终端下面一不小心按下了ctrl s,整个终端就锁住了,不知道原油的同学可能会以为终端卡死了,找不到原因只好关闭终端重新打开,然后下意识还不忘吐槽一句,垃圾ubuntu,动不动卡死。 事实上ctrl s在终端下是…...

【Vue】Vue的简单介绍与基本使用

一、什么是VueVue是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。1.构建用户界面传统方…...

网络知识篇

网络知识篇 局域网 当多台计算机或设备通过同一物理或逻辑连接(例如以太网或Wi-Fi网络)连接在一起,并且它们可以相互通信时,就构成了一个局域网(Local Area Network,LAN)。 子网划分 为了更…...

python 连接数据库

文章目录同步操作同步连Mysql同步连redis同步连mongodb异步操作异步连mysql异步连redis异步连mongodb同步操作 同步连Mysql python 连接mysql可以使用pymysql、mysqlclient等。 安装: # win pip install pymysql 连接mysql: # __author__ "laufing"…...

一文讲明白一致性hash算法

一致性Hash算法常用来解决数据分片时的数据扩容/缩容的性能问题。 一、业内数据分片用的Hash算法,将节点的hash值对节点数取余。 存取通过key / value的方式对节点取余。 二、数据分片使用hash算法的优缺点: 优点:简单,方便。 缺…...

Java分布式解决方案(一)

随着互联网的不断发展,互联网企业的业务在飞速变化,推动着系统架构也在不断地发生变化。 如今微服务技术越来越成熟,很多企业都采用微服务架构来支撑内部及对外的业务,尤其是在高 并发大流量的电商业务场景下,微服务…...

设备树系统学习(二)设备树的节点和属性

一、节点 1.节点命名格式 格式:<name>[@<unit-address>] name:是一个简单的 ASCII 字符串,长度最多为 31 个字符,节节点是根据它所代表的设备类型来命名的,比如 “gpu” 就表示这个节点是 gpu外设。 unit-address:一般表示设备的地址或寄存器首地址,可以为…...

【数据结构】二叉树的基本操作中的一些易错点

文章目录前言一、求二叉树节点个数二、求树的叶子结点个数三、求树的高度四、二叉树查找值为x的结点总结前言 笔者整理出了一些关于萌新在入门二叉树时容易犯的一些错误&#xff0c;你也来试试自己会不会掉到这些坑里把~ 一、求二叉树节点个数 错误示例&#xff1a; int Tre…...

在线图书借阅网站( Python +Vue 实现)

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括&#xff1a;首页、图书详情页、用户中心模块。后台功能包括&#xff1a;总览、借阅管理、图书管理、分类…...

不平衡数据集的建模的技巧和策略

不平衡数据集是指一个类中的示例数量与另一类中的示例数量显著不同的情况。 例如在一个二元分类问题中&#xff0c;一个类只占总样本的一小部分&#xff0c;这被称为不平衡数据集。类不平衡会在构建机器学习模型时导致很多问题。不平衡数据集的主要问题之一是模型可能会偏向多数…...

3. 算法效率

同一个问题的不同算法在性能上的比较,现在的方法主要是算法时间复杂度。算法效率是算法操作(operate)或处理(treat)数据的重复次数最小。 例题选自《编程珠玑》第8章,算法设计技术。 这个问题是一维模式识别(人工智能)中的一个问题。 输入有n个元素的向量,输出连续子向…...

仪表放大器放大倍数分析-运算放大器

仪表放大器是一种非常特殊的精密差分电压放大器&#xff0c;它的主要特点是采用差分输入、具有很高的输入阻抗和共模抑制比&#xff0c;能够有效放大在共模电压干扰下的信号。本文简单分析一下三运放仪表放大器的放大倍数。 一、放大倍数理论分析 三运放仪表放大器的电路结构…...

laravel8多模块、多应用和多应用路由

1、安装多应用模块 composer require nwidart/laravel-modules2、执行命令&#xff0c;config文件夹下生成一个modules.php配置文件 php artisan vendor:publish --provider"Nwidart\Modules\LaravelModulesServiceProvider"3、修改config文件夹下的modules.php&am…...

【Java学习笔记】6.Java 变量类型

Java 变量类型 在Java语言中&#xff0c;所有的变量在使用前必须声明。声明变量的基本格式如下&#xff1a; type identifier [ value][, identifier [ value] ...] ;格式说明&#xff1a;type为Java数据类型。identifier是变量名。可以使用逗号隔开来声明多个同类型变量。 …...

Promise对象状态属性 工作流程 Promise对象的几个属性

Promise 对象状态属性介绍 实例对象中的一个属性 PromiseState pending 1、pending 变为 resolved / fullfilled 成功 2、pending 变为 rejected 失败 说明&#xff1a;只有这2种&#xff0c;且一个promise对象只能改变一次 无论变为成功还是失败&#xff0c;都会有一个结果…...

webgpu思考obj携带属性

今天在搞dbbh.js的时候&#xff0c;想到一个问题&#xff0c;啥问题呢&#xff0c;先看看情况 画2个材质不相同的box的时候 首先开始createCommandEncoder,然后beginRenderPass&#xff0c;分歧就在这里了 第一个box,他有自己的pipeline&#xff0c;第二个也有&#xff0c;那么…...

设计模式(只谈理解,没有代码)

1.什么是设计模式设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。2.为什么要学习设计模式看懂源代码&#xff1a;如果你不懂设计模式去看Jd…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...