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


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.