问题标签 [api-design]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
idioms - API 设计:“容错”是好事吗?
我已经整合了许多有用的答案,并在下面提出了我自己的答案
例如,我正在编写一个Foo
需要显式初始化和终止的 API。(应该与语言无关,但我在这里使用 C++)
显然,我们的库并不关心多线程、重入或诸如此类的东西。假设我们的Init
函数应该只被调用一次,再次使用任何其他输入调用它都会造成严重破坏。
将此信息传达给我的来电者的最佳方式是什么?我可以想到两种方法:
- 在里面
InitLibrary
,我assert
有一些静态变量会责怪我的调用者初始化两次。 - 在里面
InitLibrary
,我检查了一些静态变量,如果我的库已经初始化,我会默默地中止。
方法#1 显然是显式的,而方法#2 使它对用户更友好。我认为方法#2 的缺点可能是我的调用者不会意识到InitLibrary
不应调用两次的事实。
每种方法的优缺点是什么?有没有更聪明的方法来颠覆这一切?
编辑
我知道这里的例子很做作。正如@daemon 指出的那样,我应该初始化自己而不打扰调用者。然而实际上,有些地方我需要更多信息来正确初始化自己(注意使用我的变量名someMagicInputRequiredAtRuntime
)。这不仅限于初始化/终止,而是存在困境的其他情况,无论我应该选择引用并引用“容错”还是糟糕地失败。
java - 如果任务抛出 RuntimeException/Error,ScheduledExecutorService.scheduleAt* 方法是否应该重新安排任务?
前几天我在我的应用程序中实现了一项重要的服务,无论如何它都应该继续运行。所以我使用了以下构造:
...只是发现当 importantPeriodicTask 准确地抛出 RuntimeException 或 Error 时,ScheduledExecutorService
将停止执行此任务(它们将停止被安排)。
这当然正是 javadoc 所说的:
如果任务的任何执行遇到异常,则后续执行将被抑制。
真让我感到羞耻,但我不明白为什么作者会ScheduledExecutorService
这样实施。
当然,通常不应捕获 RuntimeException 或 Error,尤其是 Error。但实际上,尤其是在 RuntimeException 的情况下,事实是它们在生产部署中非常普遍,我觉得几乎总是希望当特定操作失败时,应用程序本身不应该因为孤立的错误而失败。
确实,抑制一个周期性任务不会影响其他类型的周期性任务。但是鉴于大多数周期性任务的性质,这些任务不应该被视为“服务”,而不是孤立的任务吗?
换句话说,不应该只有一个实例importantPeriodicTask
失败,并且任务本身继续被重新安排吗?
c++ - 私有类函数与未命名命名空间中的函数
我发现自己倾向于没有私有类函数。如果可能的话,私有类函数的所有候选者,而不是我放入未命名的命名空间并将所有必要的信息作为函数参数传递。我没有一个合理的解释为什么我要这样做,但至少在我看来它看起来更自然。因此,我需要在头文件中公开较少的内部细节。
你的意见是什么——这是正确的做法吗?
java - 改造 void 方法以返回其参数以促进流畅性:破坏性变化?
“API 设计就像性:犯一个错误并在你的余生中支持它” (推特上的乔什·布洛赫)
Java 库中有很多设计错误。Stack extends Vector
(讨论),我们不能在不造成损坏的情况下解决这个问题。我们可以尝试弃用Integer.getInteger
(讨论),但它可能会永远存在。
尽管如此,可以在不造成损坏的情况下进行某些类型的改造。
Effective Java 第 2 版,第 18 项:优先使用接口而不是抽象类:现有类可以很容易地改造以实现新接口”。
示例:String implements CharSequence
、Vector implements List
等。
Effective Java 第 2 版,第 42 条:明智地使用可变参数:您可以改进现有的方法,该方法将数组作为其最终参数以采用可变参数,而不会对现有客户端产生影响。
一个(不)著名的例子是Arrays.asList
,它引起了混乱(讨论),但没有破坏。
这个问题是关于一种不同类型的改造:
你能在void
不破坏现有代码的情况下改造一种方法来返回一些东西吗?
我最初的预感是肯定的,因为:
- 返回类型不影响编译时选择的方法
- 请参阅:JLS 15.12.2.11 - 未考虑返回类型
- 因此,更改返回类型不会更改编译器选择的方法
- 改装从
void
退货是合法的(但不是相反!)
- 即使您使用反射,
Class.getMethod
也无法区分返回类型
但是,我想听听其他在 Java/API 设计方面更有经验的人进行更彻底的分析。
附录:动机
正如标题中所建议的,一个动机是促进流畅的界面风格编程。
考虑这个简单的代码片段,它打印一个打乱的名字列表:
已经Collections.shuffle(List)
声明返回输入列表,我们可以这样写:
如果它们要返回输入列表而不是,等,那么使用其他方法Collections
会更愉快。事实上,拥有和返回尤其不幸,因为它剥夺了我们编写表达代码的能力,void
例如像这样:reverse(List)
sort(List)
Collections.sort
Arrays.sort
void
当然,这种void
阻碍流畅性的返回类型不仅限于这些实用方法。这些java.util.BitSet
方法也可以编写为返回this
(alaStringBuffer
和StringBuilder
) 以促进流畅性。
不幸的是,与StringBuilder
/不同StringBuffer
,所有的BitSet
mutators 都返回void
。
相关话题
api-design - 网站使用自己的 API 是一种好习惯吗?
在开发网站时开发 API 是否是一种好习惯,以便网站本身实际使用 API?或者如果选择这样做会影响性能吗?
例如,有谁知道 Facebook 或 Digg 等成熟网站是否使用自己的 API 来进行 CRUD(创建、读取、更新、删除),还是有自己的后端?谢谢
c - 编写 API 以与串行端口上连接的设备进行通信
恐怕我的问题中有几个术语是错误的。请容忍我,并在我错的地方纠正我。
我必须编写一个库/程序,它将提供一组功能来操作连接在串行端口上的读卡器。就像弹出插入其中的卡一样,用户只需调用他的代码,例如,
cardEject()
和其他功能将自己处理打开串口,向其写入数据,检查确认,检查错误代码,在失败时重新发送命令等。我很清楚与串口上的设备通信。
我的问题是,在编写完所有这些函数并对其进行测试之后,我应该如何将它们提供给用户。
我应该给他一个头文件(.h
)和一个目标文件(.o
)吗?这样他就可以在编译他的实际程序时链接到该对象。
我应该提供一个静态库(.a
)吗?
哪个是更好的主意?
每个函数打开串口然后关闭它是一个好主意吗?或者initCardReader()
打开它,设置它的属性并closeCardReader()
应该关闭它?所有其他函数只能在initCardReader()
?
现在是一个愚蠢但真实的问题 :-) 用于此类程序的术语是什么?它是驱动程序、库还是设备接口?此类项目的正确标签是什么?
谢谢你的时间。
编辑
感谢大家指导我。非常感谢。
这个 API 必须成为更大项目的一部分。事实上,我也会从事那个项目。但是这个 API 很有可能会在有我或没有我的其他项目中使用。我认为,考虑到可能在其他项目中使用,图书馆更有意义。如果我错了,请纠正我。
java - 使用注解确保方法返回的值不被丢弃
String
在 Java 中是不可变的。从广义上讲,以下代码段是“错误的”。
尽管这是“错误的”,但代码还是可以编译和运行,这可能会让许多初学者感到困惑,他们必须要么被告知错误是什么,要么通过查阅文档自己找出答案。
阅读文档是理解 API 的重要组成部分,但我想知道这是否可以通过额外的编译时检查来补充。特别是,我想知道是否可以使用 Java 的注释框架来强制某些方法返回的值不被忽略。然后 API 设计者/库作者将在他们的方法中使用此注释来记录不应忽略哪些返回值。
一旦 API 补充了这个注解(或者可能是另一种机制),那么每当用户编写上述代码时,它就不会编译(或者会发出严厉的警告)。
那么这可以做到吗,你将如何做这样的事情?
附录:动机
显然,在一般情况下,Java应该允许忽略方法的返回值。List.add
(总是 true
)、 (前一个值)之类的方法的返回值System.setProperty
可能在大多数情况下都可以安全地忽略。
但是,也有许多方法的返回值不应被忽略。这样做几乎总是一个程序员错误,或者不是正确使用 API。其中包括:
- 不可变类型(例如
String
,BigInteger
等)上的方法返回操作的结果,而不是改变调用它的实例。 - 其返回值是其行为的关键部分且不应被忽略的方法,但人们有时还是会这样做(例如
InputStream.read(byte[])
返回读取的字节数,不应假定为数组的整个长度)
目前我们可以编写忽略这些返回值的代码,并让它们在没有警告的情况下编译和运行。静态分析检查器/错误查找器/样式执行器/等几乎可以肯定地将这些标记为可能的代码异味,但如果这可以由 API 本身(可能通过注释)强制执行,这似乎是合适/理想的。
一个类几乎不可能确保它总是“正确”使用,但是它可以做一些事情来帮助指导客户正确使用(参见:Effective Java 2nd Edition,Item 58: Use checked exceptions for recovery conditions and编程错误的运行时异常和第 62 条:记录每个方法抛出的所有异常)。有一个注释可以强制客户端不忽略某些方法的返回值,并让编译器在编译时以错误或警告的形式强制执行,似乎符合这个想法。
附录 2:片段
以下是一个初步尝试,简洁地说明了我想要实现的目标:
上面的代码编译并运行良好(见 ideone.com)。我怎样才能让它不是这样?如何分配我想要的语义@Undiscardable
?
rest - 为持久任务创建 REST API 的最佳方式?
假设我有 2 台服务器。
第一个是提供一些计算的服务,可以持续很长时间(几分钟到几小时)。
第二台服务器将使用此服务来计算一些数据。
我正在尝试为第一台服务器设计一个 REST API,到目前为止一切都很好。但我想听听一些关于如何在长期任务完成后对通知建模的意见。
到目前为止,我考虑了两种方法:
- 轮询 - 第二台服务器将不时询问结果。
- 回调 - 第二台服务器将为第一个服务器设置一个 uri,以便在完成后调用。但这在 REST API 中有点味道。
你怎么看?
windows - 为什么 Windows API 不如其他 API 直接?
在我看来,Windows API 并不像您想象的那么简单。对我来说,它们是有点复杂的时尚?
这是保持向后兼容性的效果吗?
Microsoft 的主要目标是推动开发人员使用 ATL/MFC、VB 和/或 .net 等更高级别的抽象吗?
这是我第一次使用 Win32 API,类似FindFirstFile
的调用让我坚信 UNIX 哲学,其中 API 似乎以 10% 的工作量完成了 90% 的灵活性。再一次,似乎有一些独特的情况需要fcntl
在 *nix 类型的机器上使用调用,它们将成为 Windows 上实际调用的一部分。
...还是我只是缺少一个基本范式?
f# - 为什么 F# 的 Collections.Seq 模块基本上重新实现了所有的 Enumerable 扩展方法?
为什么Collections.Seq
模块有很多方法看起来等同于声明的扩展方法System.Linq.Enumerable
?为什么 F# 的设计者觉得需要为所有这些创建一个新的命名空间和新的/不同的名称,而不是重用 .NET 中已经存在的东西?
(如果他们需要一些额外的方法,为什么不直接将它们添加到System.Linq.Enumerable
?)