未加星标

Notes on API design in Go

字体大小 | |
[开发(python) 所属分类 开发(python) | 发布者 店小二03 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

At work, I have a working student who’s implementing some features in the various Golang applications that I build and maintain. I’m trying to pass some of my experience with real-world programming on to him, and one particular pull request review escalated into a blog post on API design, so I might as well share it here for archival purposes (and to fill the desolate wasteland that is my RSS feed).

The concern of the pull request was to add a function to a utility library that implements exponential backoff. His proposed API looked like:

//Retry takes a function (action) that returns an error, and two int64 values (x, y) as //parameters and creates a retry loop with an exponential backoff such that on failure (error return), //the action is called again after x seconds and this is incremented by a factor of 2 until y minutes //then it is keeps on repeating after y minutes till action succeeds (no error). func Retry(action func() error, x, y time.Duration) { ... } The original comment

When you have a function that takes another function, I like to place the function argument at the end of the argument list. When the function argument is a long anonymous function, the other arguments otherwise get separated from the function call:

err := doSomething(function() error { if aLotOfThings.Happen() { in.ThisFunction(AndMaybe { There: "are", More: "braces", And: "stuff", }) } then(Lines(100).DownBelow()) itIsNotClear = true }, 2, 4) //...which function call these arguments belong to

vs.

err := doSomething(2, 4, function() error { ... ... ... })

Finally, I would suggest a different API design altogether. The problem with function arguments is that it’s sometimes difficult to tell from context what the arguments mean. An extreme example (which I sadly cannot find on Google right now) is the Win32 API function for starting a new process which takes 22 arguments and it looks something like:

StartProcess("C:\windows\system32\rundll.exe", null, null, null, null, null, null, null, null, true, true, false, false, null, false, true);

So unless you have the API docs open on your other monitor, it’s pretty impossible to tell what each of these arguments mean. To be able to give meaningful names to arguments, it’s common practice in Go APIs to collect all these switches and configuration options into an Options struct:

type RetryOptions struct { BackoffFactor int MaxInterval time.Duration } func Retry(opts RetryOptions, action func() error) { ... } //usage example: Retry(RetryOptions { BackoffFactor: 2, MaxInterval: 5 * time.Second }, func() { ... })

That’s much more verbose, but also much more obvious when you’re reading it. It also has the advantage that you can later add new fields to RetryOptions without breaking existing users of your API. (If you look at the API of Schwift , you’ll see these Options types all over the place for this reason.)

Finally, here’s the API that I would propose:

type RetryStrategy interface { RetryUntilSuccessful(action func() error) } type ExponentialBackoff struct { Factor int MaxInterval time.Duration } func (eb ExponentialBackoff) RetryUntilSuccessful(action func() error) { ... } //usage example: ExponentialBackoff { Factor: 2, MaxInterval: 5 * time.Second, }.RetryUntilSuccessful(func() error { ... })

This makes it easy to later add other implementations for type RetryStrategy (e.g. one that just retries for a given number of times and then gives up). It also allows other parts of the program to take a RetryStrategy as a parameter.

The morale

A good API design is based on two things: First, there are some fundamental values that the developer tries to optimize for. In my case, that’s extensibility and backwards-compatibility. (I’ve been bitten by updates with breaking changes a few times too often and don’t want to install this pain on others, or on myself.) Second, the language features inform how to translate these values into an API design. In python, I wouldn’t work with Options types, because Python provides kwargs which serve the same purpose and are more idiomatic.

Created: Thu, 09 Aug 2018 11:14:32 UTC

本文开发(python)相关术语:python基础教程 python多线程 web开发工程师 软件开发工程师 软件开发流程

tags: function,API,error,func,null,action,arguments
分页:12
转载请注明
本文标题:Notes on API design in Go
本站链接:https://www.codesec.net/view/586417.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(python) | 评论(0) | 阅读(73)