惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks
B
Blog
腾讯CDC
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - Franky
罗磊的独立博客
月光博客
月光博客
Jina AI
Jina AI
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Docker
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
G
Google Developers Blog
V
Visual Studio Blog
I
InfoQ
有赞技术团队
有赞技术团队
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 Golang CAS 操作是怎么实现的 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例)
编写可维护的函数
Jiajun Huang · 2021-02-21 · via Jiajun的技术笔记

编写可维护的函数

之所以写这篇,是因为看到 Dave Cheney 的一篇博客,深有同感: https://dave.cheney.net/2019/09/24/be-wary-of-functions-which-take-several-parameters-of-the-same-type

日常开发中很有可能会遇到这样的傻bug:参数写反了。Go/Kotlin等强类型语言中,当连续几个参数的类型是一样时, 类型检查就没法帮我们避坑,而Python中则更是如此,无论传什么类型进去都可以,type hint在实际项目中的使用还不够广泛, 再加上Python和Kotlin都有默认参数。

我们先来回顾一下这三中语言中的此类问题。

  • Go:

    package main
    
    func foo(a, b int) {
    	println(a, b)
    }
    
    func main() {
    	foo(1, 2)
    }
    

Go语言中无法辨别两个同类型的参数传递的先后顺序。但是Go不支持keyword arguments,所以少了一些问题。

  • Python:

    def foo(first, second, third=3, fourth=4):
    print(first, second, third, fourth)
    
    
    foo(5, 4, 5)
    

Python过于灵活,keyword arguments也可以用普通参数的形式传进去。

  • Kotlin:

    fun foo(first: Int, second: Int, third: Int=3, fourth: Int=4) {
    println("$first, $second, $third, $fourth")
    }
    
    fun main(args: Array<String>) {
    foo(1, 2, 4)
    }
    

Kotlin这点与Python一致。

这还只是一个简单的例子,当我们遇到一些函数比如 copymemmove 这样的参数,则必须要打开manual来看看函数的签名, Dave给了一种解决方案:

type Source string

func (src Source) CopyTo(dest string) error {
	return CopyFile(dest, string(src))
}

func main() {
	var from Source = "presentation.md"
	from.CopyTo("/tmp/backup")
}

这是一种方案,但是如果都这么写的话,似乎又有点麻烦。我个人的习惯是在设计此类函数时,一定要保证签名的统一性,例如 在Go Web项目中,我们经常会需要返回成功或者失败,通常来说成功我们需要定制的内容只有返回结果,而失败时,我们则希望 定制返回状态码,原因和结果。如果是这样,那么就容易造成 SuccessFail 两个函数的签名不一致,导致每次使用时, 都可能要去查一查怎么用。所以我通常会为了可维护性而牺牲一下,多写一点代码,定义如下:

package handlers

import (
	"github.com/gin-gonic/gin"
)

func Fail(c *gin.Context, code int, msg string, result interface{}) {
	c.JSON(code, gin.H{"code": code, "msg": msg, "result": result})
}

func Success(c *gin.Context, code int, msg string, result interface{}) {
	c.JSON(code, gin.H{"code": code, "msg": msg, "result": result})
}

这样子无论是成功时,还是失败时,签名顺序都是一样的,当然,如果在Python或者Kotlin这种支持keyword arguments的语言中, 可以给 codemsg 一个默认值,这样子就只有在需要定制的时候,才需要单独传参数。例如:

def success(result, code=200, msg=""):
    pass


def fail(result, code=400, msg=""):
    pass

最后,不犯此类错误的一个帮手就是代码补全提供的类型,一定要看清楚然后再写(如果项目中统一了我所说的签名统一性,那么就 可以在这一点上提升效率了)。