Swift函数与计算属性


26

假设我有一个Event如下课程:

class Event {
    private var attendees: [Person] = []

    // Case 1
    //*******
    // Should I use a func…
    func countOfAttendees() -> Int {
        return attendees.count
    }

    // …or a var
    var countOfAttendees: Int {
        return attendees.count
    }

    // Case 2
    //*******
    // Should I use a func…
    func countOfPaidAttendees() -> Int {
        return attendees.filter({$0.hasPaid}).count
    }

    // …or a var
    var countOfPaidAttendees: Int {
        return attendees.filter({$0.hasPaid}).count
    }
}

在上述两种情况下使用函数计算属性是最佳实践吗?


2
stackoverflow.com/questions/24035276/… ...简而言之:“让函数成为函数,让属性成为属性。”
罗伯特·哈维

Answers:


14

遵循统一访问原则

模块提供的所有服务都应通过统一的符号表示,无论是通过存储还是通过计算实现,都不应背叛

对我来说,这意味着我不会编写不带任何参数并返回值的函子。我总是使用计算属性。这样,如果我以后决定将计算的属性更改为存储的属性,则可以这样做,而不必急于删除应用程序中各处的parens,也不需要单独的“ getter”方法仅返回存储的值属性,恕我直言似乎很浪费。

而且,如果我将存储的属性更改为计算的属性,则不必在其末尾以及应用程序中使用的任何地方添加括号。


我最初使用@Anton的复杂性答案,但实际上我意识到这是我的方法……默认情况下是属性。
Ashley Mills

17

我会说这取决于计算的复杂性与使用频率。

  • 如果为O(1)/ *,则使用计算属性。
  • 如果是O(N)+/ rare-use,则使用function。
  • 如果是O(N)+/ frequent-use,请考虑是否将来会决定使用缓存或其他“智能”技术来补偿复杂性,如果“是”,则使用属性,如果“不,不,它很重”,则使用函数。

2
有趣的是我是如何开始使用相同的推理的。如果您“可以”给人以属性的印象,即使您必须进行轻量级处理,只要它不更改对象,就可以使其成为属性。
Dielson Sales

9

我最近开始学习Kotlin,他们对何时使用计算属性有很好的启发:

功能与属性

在某些情况下,不带参数的函数可能会与只读属性互换。尽管语义相似,但是在何时首选一个相对另一个上有一些样式约定。

当基础算法满足以下条件时,将属性优先于函数:

  • 不扔
  • 具有O(1)复杂度
  • 计算便宜(或在第一次运行时计算)
  • 通过调用返回相同的结果

- https://kotlinlang.org/docs/reference/coding-conventions.html


“不扔”对于Swift也很重要,因为属性不能扔(还?)
alejandromp

从文档中删除了“具有O(1)复杂度”
Mahmoud Shahoud,

7

在Swift中,没有参数的函数和计算出的属性具有几乎相同的功能(可能会有区别,没有参数的函数也是闭包,而计算出的属性不是闭包)。

区别在于语义上。如果您的代码执行了一个动作并返回了该动作结果的描述,那么我将使用一个函数。如果您的代码计算出一个属性,但是从用户的角度来看,这可能是一个存储的属性,或者可能是一个存储的属性,需要先更新某些缓存的值,那么我将使用一个计算的属性。

很大的不同:如果两次调用函数或计算所得的属性,会发生什么?对于计算的属性,我期望x =属性;y =属性与x =属性具有完全相同的行为;y = x,但运行速度可能会稍微慢一点。对于函数,如果行为不同,我不会感到惊讶。


4

使用countOfAttendeescountOfPaidAttendees()


计算变量是每次访问时都会返回计算值的变量。也就是说,它不存储值。在内部,它是作为功能实现的。

函数有什么区别?

  • 从语义上说,变量是状态,函数是动作。
  • 一个功能调节对私有存储的访问。计算出的变量可以更紧凑的方式执行相同操作。实例
  • 计算变量可以与KVO一起使用,作为#keypath传递,并具有观察的便利:willSet,didSet。

您应该在以下情况下使用变量

  • 它不会抛出
  • 它返回一个简单的属性
  • 它的名字没有副作用或动词
  • 它是O(1),也就是说,它不会产生可观的成本。在您的示例中,它将为O(n)。
  • 它是幂等的。多个相同的调用返回相同的值或将对象设置为相同的状态。

与变量优先于函数无关的理由

  • 计算变量可避免输入()。但是,清晰度比简洁更重要,因此这是一个微不足道的论点。
  • 只读变量可以覆盖为读/写。一个函数指示它始终是只读的。但是,Apple对只读变量(例如array.count)使用属性。如有疑问,请寻求与平台的一致性。

资源资源

从  WWDC 2014-204开始可可中的新增功能  > 24:40何时使用@property

将属性用于与对象的值或状态或其与其他对象的关系有关的任何事情。不良候选人:

  • 执行操作的方法:加载,解析,切换等。他们的名字中有动词。
  • 生成器:init,复制,枚举等。这些方法不是幂等的。
  • 更改状态的方法:nextObject。

来自  Erica Sadun的Swift Style  >计算属性与方法

属性表示实例的固有质量,而方法则执行操作。

  • 方法有参数;属性没有。对于有副作用的任何呼叫,都建议使用方法。如果方法执行某项操作(例如,它加载,解析,切换或打印)或具有动词名称,则它不应是属性。
  • 首选属性以获取和/或设置简单值。
  • 属性应表示类型实例的语义固有质量。
  • 属性允许您通过willSet和didSet添加观察者。与存储实例属性不同,必须始终为存储类型属性赋予默认值。

从  Kotlin编码约定>函数到属性。参见上面丹尼尔的答案

没有相关信息的其他资源:


3

我会用一个func。无需计算属性,面向对象的编程就可以正常工作。由于返回的是经过计算/过滤的值,因此某些人可能会认为计算出的属性感觉正确。但是,这是我的抱怨,如果您这样做,那么可读性会受到打击,因为它感觉像是一种价值。

在这种情况下,尝试分配计算值没有任何意义(幸运的是,IDE帮助我们避免了这种情况),但是如果我尝试分配计算后但看起来像值的东西怎么办?

event.countOfAttendees = 0; // not possible

使用func时,调用者知道您不是直接处理值:

event.countOfAttendees()

我认为,如果它是一个行为对象,它应该看起来像它的行为,而不是看起来像一个数据结构。如果您的对象很笨,没有任何行为,那么为什么要封装它呢?在这种情况下,您不妨让与会者公开

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.