是否应该仅通过查看代码就总是知道API在做什么?


20

最近,我一直在开发自己的API,随着对API设计的投入,我一直对如何改善API设计非常感兴趣。

出现了几个方面(不是我的API用户使用,而是在我对该主题的观察讨论中):一个人应该只通过查看调用API的代码来知道它在做什么

例如,请参见GitHub上有关此讨论的讨论,例如:

foo.update_pinned(true, true);

仅仅通过查看代码(不知道参数名称,文档等),就无法猜测它会做什么-第二个参数是什么意思?建议的改进措施如下:

foo.pin()
foo.unpin()
foo.pin_globally()

这就清除了一切(我猜第二个参数是是否全局固定foo),在这种情况下,我同意后者肯定会有所改进。

但是我相信,在某些情况下,设置不同但与逻辑相关的状态的方法最好作为一个方法调用而不是单独的方法公开,即使您仅通过查看代码也不知道它在做什么。(因此,您必须求助于参数名称和文档以查明-如果我不熟悉API,我个人将始终这样做)。

例如,我SetVisibility(bool, string, bool)FalconPeer上公开了一种方法,并且我承认只看了一下这一行:

falconPeer.SetVisibility(true, "aerw3", true);

您将不知道它在做什么。它设置了3个不同的值来控制falconPeer逻辑上的“可见性” :仅接受接受密码的加入请求,然后回复发现请求。将其分为3个方法调用可能会导致API的用户设置“可见性”的一个方面而忘记设置其他设置,而我仅通过公开一种方法来设置“可见性”的所有方面而迫使他们考虑。此外,当用户想要更改一个方面时,他们几乎总是会想要更改另一个方面,现在可以在一个呼叫中进行更改。


13
这正是某些语言使用命名参数(有时甚至强制使用命名参数)的原因。例如,你可以组一个简单的很多设置update方法:foo.update(pinned=true, globally=true)。或:foo.update_pinned(true, globally=true)。因此,对您的问题的回答也应考虑语言功能,因为针对语言X的良好API可能对语言Y不利,反之亦然。
Bakuriu 2014年

同意-让我们停止使用布尔值:)
2014年


@Bakuriu即使C是枚举,即使它们是伪装的整数。我认为布尔值是不错的API设计,在现实世界中没有任何语言。
Doval 2014年

1
@Doval我不明白你在说什么。在这种情况下,我之所以使用布尔值是因为OP使用了布尔值,但是我的观点与传递的值完全无关。例如:setSize(10, 20)不如setSize(width=10, height=20)或可读random(distribution='gaussian', mean=0.5, deviation=1)。在具有强制命名参数的语言中,布尔值可以传达与使用枚举/命名常量完全相同的信息量,因此它们在API中可能是很好的。
Bakuriu 2014年

Answers:


27

您不希望将其分为三个方法调用是完全可以理解的,但是除了布尔参数外,您还有其他选择。

您可以使用枚举:

falconPeer.SetVisibility(JoinRequestOptions.Accept, "aerw3", DiscoveryRequestOptions.Reply);

甚至是一个标志枚举(如果您的语言支持):

falconPeer.SetVisibility(VisibilityOptions.AcceptJoinRequests | VisibilityOptions.ReplyToDiscoveryRequests, "aerw3");

或者您可以使用Parameter对象

var options = new VisibilityOptions();
options.AcceptJoinRequests = true;
options.ReplyToDiscoveryRequest = true;
options.Password="aerw3";
falconPeer.SetVisibility(options);

参数对象模式为您提供了其他一些可能会有所帮助的优点。它使轻松传递和序列化一组参数变得很容易,并且您可以轻松为未指定的参数提供“默认”值。

或者,您可以只使用布尔参数。微软似乎一直在使用.NET Framework API进行操作,因此您可以耸耸肩说:“如果对他们足够好,那么对我也足够”。


OP指出,参数对象模式仍然存在问题:“ 将其分为3个方法调用可能会导致API用户设置“可见性”的一个方面而忘记设置其他方面 ”。
罗德2014年

是的,但是我确实认为这会使情况变得更好(如果不是完美的话)。如果用户被迫实例化并传入VisibilityOptions对象,则可能(幸运的是)提醒他们他们可能想要设置VisibilityOptions对象上的其他属性。使用三种方法调用方法时,他们仅需提醒他们的是对其所调用方法的注释。
BenM 2014年

6

显然,规则总是有例外,但是正如您自己很好地解释的那样,拥有可读的API有某些优点。布尔参数特别麻烦,因为1)它们没有揭示意图,2)暗示您调用一个函数,实际上应该有两个,因为根据boolean标志会发生不同的事情。

主要问题是:您想投入多少精力使您的API更具可读性?越是外向,就越容易证明需要付出更多的努力。如果它是仅由另一个单元使用的API,那么没什么大不了的。如果您要谈论的是REST API,打算让整个世界都在此之上,那么您不妨投入更多的精力,使其更加易于理解。

对于您的示例,有一个简单的解决方案:显然,在您的情况下,可见性不仅仅是对还是错。相反,您拥有一整套被视为“可见性”的东西。一种解决方案可能是引入类似Visibility类的内容,其中涵盖所有这些不同种类的可见性。如果您进一步应用Builder模式来创建它们,则可能会得到如下代码:

Visibility visibility = Visibility.builder()
  .acceptJoinRequests()
  .withPassword("aerw3")
  .replyToDiscoveryRequests()
  .build();
falconPeer.setVisibility(visibility);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.