Свифт-функции против вычисляемых свойств


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 случаях, указанных выше?


2
stackoverflow.com/questions/24035276/… ... Вкратце: «Пусть ваши функции будут функциями, а ваши свойства будут свойствами».
Роберт Харви

Ответы:


14

Следуйте принципу единого доступа ,

Все услуги, предлагаемые модулем, должны быть доступны через единую систему обозначений, в которой не указано, реализованы ли они через хранилище или посредством вычислений.

Для меня это означает, что я не пишу функции, которые не принимают аргументов и возвращают значение. Я всегда использую вычисленные свойства. Таким образом, если позже я решу изменить вычисляемое свойство на хранимое свойство, я могу сделать это без необходимости удалять символы везде в моем приложении и без отдельного метода «getter», который просто возвращает значение хранимого собственность, которая кажется довольно расточительной ИМХО.

И если я изменяю сохраненное свойство на вычисляемое, мне не нужно добавлять в конец его значение, а также везде, где оно используется в приложении.


Первоначально я пошел с ответом о сложности @ Антона, но на практике я понял, что это то, как я это делаю ... свойства по умолчанию.
Эшли Миллс

17

Я бы сказал, что это зависит от сложности расчета и частоты использования.

  • Если это O(1)/ *, то используйте свойство computed.
  • Если это O(N)+/ rare-use, то используйте функцию.
  • Если это O(N)+/ frequent-use, подумайте, возможно ли в будущем вы решите использовать кэширование или другие «умные» методы для компенсации сложности, если «да», тогда используйте свойство, если «нет-нет-нет, это просто тяжело», то используйте функцию ,

2
Забавно, как я начал делать это, используя те же рассуждения. Если вы «можете» создать впечатление, что это свойство, даже если вам приходится выполнять легковесную обработку, если она не меняет объект, сделайте его свойством.
Продажи Dielson

9

Недавно я начал изучать Kotlin, и у них есть отличная эвристика о том, когда использовать вычисляемые свойства:

Функции против свойств

В некоторых случаях функции без аргументов могут быть взаимозаменяемыми со свойствами только для чтения. Хотя семантика схожа, существуют некоторые стилистические соглашения о том, когда отдавать предпочтение одному другому.

Предпочитайте свойство над функцией, если основной алгоритм:

  • не бросает
  • имеет сложность O (1)
  • это дешево для расчета (или выпало при первом запуске)
  • возвращает тот же результат по вызовам

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


«Не выбрасывает» также важно для Swift, потому что свойства не могут выбрасывать (пока?)
alejandromp

«имеет сложность O (1)» удалено из документации
Махмуд Шауд

7

В Swift функции без параметров и вычисляемые свойства имеют почти одинаковые возможности (может быть разница, что функция без параметра также является замыканием, а вычисляемое свойство - нет).

Разница семантически. Если ваш код выполняет действие и возвращает, например, описание результата этого действия, я бы использовал функцию. Если ваш код вычисляет свойство, но, с точки зрения пользователя, это может быть сохраненное свойство или, может быть, хранимое свойство, которое сначала требует обновления какого-либо кэшированного значения, тогда я бы использовал вычисляемое свойство.

Большая разница: что произойдет, если вы вызовете функцию или вычисляемое свойство дважды? Для вычисляемого свойства я ожидаю, что x = свойство; свойство y = имеет то же поведение, что и свойство x =; y = x за исключением того, что он может работать чуть медленнее. Для функций я не удивлюсь, если поведение будет другим.


4

Используйте countOfAttendeesи countOfPaidAttendees().


Вычисляемая переменная - это переменная, которая возвращает вычисленное значение каждый раз, когда к ней обращаются. То есть он не хранит значение. Внутренне это реализовано как функция.

В чем разница с функцией?

  • Семантически, переменная - это состояние, функция - это действие.
  • Функция регулирует доступ к частному хранилищу. Вычисляемая переменная может сделать то же самое в более компактном виде. Пример .
  • Вычисляемая переменная может использоваться с KVO, передаваться как #keypath и имеет возможности для наблюдения: willSet, didSet.

Вы должны использовать переменную, когда

  • это не бросает
  • возвращает простое свойство
  • у него нет побочного эффекта или глагола в его названии
  • это O (1), то есть оно не несет значительных затрат. В вашем примере это будет O (n).
  • это идемпотент. Несколько идентичных вызовов возвращают одно и то же значение или устанавливают объект в одно и то же состояние.

Нерелевантные причины предпочитать переменную функции

  • Вычисляемая переменная избавляет вас от набора текста (). Однако ясность важнее краткости, поэтому это слабый аргумент.
  • Переменная только для чтения может быть переопределена как чтение / запись. Функция указывает, что она всегда только для чтения. Однако Apple использует свойства для переменных только для чтения, таких как array.count. В случае сомнений ищите согласованность с платформой.

Ресурсы

Из  WWDC 2014 - 204 Что нового в Какао  > 24:40 Когда использовать @property

Используйте свойство для всего, что касается значения или состояния объекта или его связи с другими объектами. Плохие кандидаты:

  • Методы, которые делают вещи: загрузить, разобрать, переключить,…. У них есть глаголы в его названии.
  • Генераторы: инициализация, копирование, перечисление,…. Эти методы не являются идемпотентными.
  • Методы, которые меняют состояние: nextObject.

Эрика Садун от  Swift Style  > Вычисленные свойства и методы

Свойство выражает качество, присущее экземпляру, в то время как метод выполняет действие.

  • Методы имеют параметры; свойства не Предпочитаю методы для любого вызова с побочными эффектами. Если метод что-то делает (например, загружает, анализирует, переключает или печатает) или имеет имя глагола, он не должен быть свойством.
  • Предпочитайте свойства для простых значений, которые вы можете получить и / или установить.
  • Свойства должны выражать семантическое внутреннее качество экземпляра типа.
  • Свойства позволяют добавлять наблюдателей через willSet и didSet. В отличие от свойств хранимых экземпляров, свойствам хранимых типов всегда должно быть присвоено значение по умолчанию.

Из  соглашений кодлинга Kotlin> функции против свойств . Смотрите ответ Даниила выше .

Другие ресурсы без соответствующей информации:


3

Я бы использовал func. Объектно-ориентированное программирование прекрасно работает без вычисляемых свойств. Поскольку вы возвращаете значение, которое было вычислено / отфильтровано, некоторые могут утверждать, что вычисленное свойство кажется правильным. Но вот моя жалоба: если вы делаете это, то читаемость требует успеха, потому что это похоже на значение.

В этом контексте не имеет смысла пытаться присвоить вычисленное значение (и, к счастью, IDE помогает нам избежать этого), но что если я попытаюсь присвоить что-то, что вычисляется, но выглядит как значение?

event.countOfAttendees = 0; // not possible

При использовании func вызывающая сторона знает, что вы не имеете дело со значением напрямую:

event.countOfAttendees()

Я думаю, что если это поведенческий объект, он должен выглядеть так, как будто он ведет себя, а не как структура данных. Если ваш объект тупой и не имеет никакого поведения, тогда зачем пытаться инкапсулировать его? В этом случае вы могли бы просто сделать посетителей публичными

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.