Swift中的函数命名

在本文中,我们将面临一个问题,即编写函数时应始终问自己:

我应该如何命名这个功能?

尽管这个问题看起来很简单,但正确回答它却决定了我们作为软件开发人员的职业生涯中至关重要的方面。 正如我们将看到的,它使我们的代码库更清洁,更易于使用。

如果要使用第三方库中的函数,那么可以说创建了精美标签,您会发现以下两个选项:

  // 一种 
静态函数makeLabel(withTitle标题:String)-> FancyLabel // B
静态函数configure(_ text:String)-> FancyLabel

您会更喜欢哪一个? A还是B?

如果您仍在犹豫,我会给您一个线索:常识也使我想到了选项A。


那么为什么选择A?

首先,让我们分析一下为什么B不那么好:选项B不能告诉我们我们到底在配置什么。 它是否存在? 它会创建一个新的吗? 它会返回新事物吗? 它也没有告诉我们它期望的String的含义是什么。 我们可以事先告诉我们的是,它接收一个String并返回一个FancyLabel ,但我们FancyLabel知道它的作用。 存在歧义和缺乏信息,这是我们应该避免的事情。

另一方面,选项A 对于这三件事完全是明确的

  • 工作需要什么 -标题
  • 它的作用 *- 使其成为标签
  • 结果如何创建的标签

*我们关心它做什么 (或应该做什么),而不是它如何做那件事。 我们不在乎函数的内部工作原理。 封装就是这样工作的。

仅需清楚这三点, 就可以很自然地使用此函数,因为这样我们就可以毫无误解地知道函数的作用。 就这么简单。

当我们看一下这些函数的调用方式时,将会更加清楚:

  let labelA = FancyLabel.makeLabel(withTitle:“ Hello world”)// clearlet labelB = FancyLabel.configure(“ Hello world”)//不太清楚 

现在,如果我们将该示例转换为一个由多个开发人员进行工作的真实项目,该怎么办? 您希望将函数命名为什么? A还是B? 您希望队友将他们的职能命名为什么样? A还是B?

您可以这样想: 每次定义函数时,都在定义一个将来其他人会使用的接口 。 即使它是私有的,并且即使您认为没有人会使用它,将它弄清楚也总是很重要的。 因为任何开发人员随时都可能需要处理项目中任何类的内部代码,甚至您自己也是如此。 你永远都不会知道!

总而言之,如果您在命名函数时付出了很多努力,以使它们的输入和输出是什么以及它们的作用没有歧义,那么当其他人不得不调用您的函数时,可以省去很多工作。 他们将不必完全执行它的实现。 这样,我们可以节省时间并避免可能导致错误的误解。 在代码中定义清晰的接口具有代码库的巨大好处,该代码库更易于处理,使用,定型和维护

这多余的几秒钟( 或几分钟),您需要考虑一个函数的名称是时间投入的

如果将Swift与Objective-C进行比较,尤其是从Swift 3.0开始,那么Swift中最根本的变化之一就是函数签名的工作方式。 从该版本开始,Swift不仅比其前任版本更冗长,而且更加清晰。

让我们看一个例子:

  dateLabel.text = [格式化程序stringWith 日期 :[[Date alloc] init]]]; dateLabel.text =格式化程序.string(with:Date()) 

我们在这里看到两个主要区别。 首先,在第一种情况下,创建日期实例的方式更为冗长,尽管这仅与Objective-C的冗长程度有关。 然后,我们可以看到,Objective-C版本除了很多额外的括号外,还包括Swift所没有的额外单词(日期)。

由于Swift中类型的静态性质,这种微妙但有效的更改实际上是可能的。 在该函数的Swift版本中,您无法传递不是Date另一个对象,因为编译器不允许您这样做。

此项更改很简单,但为功能签名的新世界打开了大门。 总体而言,该代码更简洁,更类似于口头语言,阅读起来也变得更加愉快。

如前所述,我们的下一步目标是修剪函数签名,使它们变得足够简短以致简洁,但仍然足够清晰以避免模糊。

最后,让我们分析一些不同函数签名的例子,看看如何通过修剪 (即删除一些单词)使它们看起来更好。

  // Signaturefunc moveView(view:UIView,toPoint point:CGPoint){...} // UsagemoveView(view:headerView,toPoint:.zero)//long️长而多余 

它可以变成:

  // Signaturefunc move(_ view:UIView,指向:CGPoint){...} // Usagemove(headerView,指向:CGPoint.zero)//👏简洁 

多亏了静态类型,我们不需要指定要在函数签名中移动视图对象。 由于该函数需要一个UIView对象,因此您唯一可以传递的就是这种对象。 同

但是也有例外。 您仍然需要指定参数的含义,因为它的类型可能不足以描述它。

  // Signaturefunc makeButton(withTitle title:String)-> UIButton {...} // Usagelet button = makeButton(withTitle:“ Function Naming”)//👍好 

让我们看看当我们在这里修剪时会发生什么……

  // Signaturefunc makeButton(带有标题:String)-> UIButton {...} // Usagelet button = makeButton(带有:“ Function Naming”)//👎不清楚 

发生这种情况是因为类型( String )与内容( title )的语义不完全匹配。 除了标题,字符串还可以表示许多其他内容。

在这种情况下,我们可以采用两种可能的方法。 我们可以通过使参数名称更显式来指定内容的语义,如上面示例的第一部分中所示,或者,我们可以创建一个更具体地描述语义的新类型 ,并将该新类型用作参数。 在示例中,我们可以创建一个Title类型来管理标题。 理想的情况是使用后者,因为您将从静态键入系统中获得更多的好处,并且您的代码最终将变得更加安全。 我建议您为此目的学习诸如幻像类型和标记类型之类的技术。

  • 命名很难。 但是,您花在这样做上的时间是值得投资的。 它减少了误解的可能性,并防止其他开发人员( 或您自己 )不得不查看所有代码的工作方式以试图理解其工作。 因此,花时间命名
  • 利用静态类型 。 使您的API简洁明了。
  • 与您的队友分享这些知识,并鼓励良好做法。
  • 实践! 实践! 实践!

请记住, 一个函数应该始终清楚三件事

  • 需要什么输入
  • 它的作用在不暴露内部运作的情况下描述过程
  • 它返回什么 输出

希望本指南对您有所帮助,并希望在下面的评论中了解您的经验和意见。 请发表评论,提出建议,如果您觉得有用,请分享🙂

感谢您的阅读。


在社交媒体平台上关注我们:
面子书:facebook.com/AppCodamobile/
Twitter:twitter.com/AppCodaMobile
Instagram的:instagram.com/AppCodadotcom

如果您喜欢这篇文章,请单击👏按钮并分享以帮助其他人找到它! 随时在下面发表评论。