Funciones rápidas frente a propiedades calculadas

26

Digamos que tengo una clase de la Eventsiguiente manera:

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
    }
}

¿Es una buena práctica usar funciones o propiedades calculadas en los 2 casos indicados anteriormente?

Ashley Mills
fuente
2
stackoverflow.com/questions/24035276/… ... En resumen: 'Deja que tus funciones sean funciones y tus propiedades sean propiedades'.
Robert Harvey

Respuestas:

14

Siga el principio de acceso uniforme ,

Todos los servicios ofrecidos por un módulo deben estar disponibles a través de una notación uniforme, que no traicione si se implementan a través del almacenamiento o el cálculo

Para mí, esto significa que no escribo funcs que no toman argumentos y devuelven un valor. Siempre uso propiedades calculadas. De esa manera, si más tarde decido cambiar la propiedad calculada en una propiedad almacenada, puedo hacerlo sin tener la necesidad de eliminar los parentes en todas partes de mi aplicación y sin tener un método "getter" separado que solo devuelva el valor de un propiedad, que parece un desperdicio bastante en mi humilde opinión.

Y si cambio una propiedad almacenada en una calculada, no tengo que agregar parens al final, y en todos los lugares donde se usa en la aplicación.

Daniel T.
fuente
Originalmente fui con la respuesta de complejidad de @ Anton, pero en la práctica me di cuenta de que esta es la forma en que lo hago ... propiedades por defecto.
Ashley Mills
17

Diría que depende de la complejidad del cálculo frente a la frecuencia de uso.

  • Si es O(1)/ *, entonces use la propiedad calculada.
  • Si es O(N)+/ rare-use, entonces use la función.
  • Si es O(N)+/ frequent-use, piense si en el futuro podría decidir usar el almacenamiento en caché u otras técnicas "inteligentes" para compensar la complejidad, si es "sí", use la propiedad, si "no-no-no, es pesado", use la función .
cortesanos
fuente
2
Es curioso cómo comencé a hacerlo usando el mismo razonamiento. Si "puede" darle la impresión de ser una propiedad, incluso si tiene que realizar un procesamiento ligero, siempre que no cambie el objeto, hágalo una propiedad.
Dielson Sales
9

Recientemente comencé a aprender Kotlin y tienen una gran heurística sobre cuándo usar propiedades calculadas:

Funciones vs Propiedades

En algunos casos, las funciones sin argumentos pueden ser intercambiables con propiedades de solo lectura. Aunque la semántica es similar, hay algunas convenciones estilísticas sobre cuándo preferir una a la otra.

Prefiere una propiedad sobre una función cuando el algoritmo subyacente:

  • no tira
  • tiene una complejidad O (1)
  • es barato de calcular (o cobra en la primera ejecución)
  • devuelve el mismo resultado sobre invocaciones

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

Daniel T.
fuente
El 'no lanza' también es importante para Swift porque las propiedades no pueden lanzar (¿todavía?)
alejandromp
"tiene una complejidad O (1)" se ha eliminado de la documentación
Mahmoud Shahoud
7

En Swift, las funciones sin parámetros y propiedades calculadas tienen casi las mismas capacidades (puede haber una diferencia de que una función sin parámetro también es un cierre, mientras que una propiedad calculada no lo es).

La diferencia es semánticamente. Si su código realiza una acción y devuelve, por ejemplo, una descripción del resultado de esa acción, entonces usaría una función. Si su código calcula una propiedad pero desde el punto de vista del usuario, esta podría haber sido una propiedad almacenada, o tal vez una propiedad almacenada que requiere actualizar primero un valor almacenado en caché, entonces usaría una propiedad calculada.

Una gran diferencia: ¿qué sucede si llama a la función o la propiedad calculada dos veces? Para una propiedad calculada, espero que x = propiedad; y = propiedad tiene exactamente el mismo comportamiento que x = propiedad; y = x, excepto que podría funcionar un poco más lento. Para las funciones, no me sorprendería si el comportamiento fuera diferente.

gnasher729
fuente
4

Uso countOfAttendeesy countOfPaidAttendees().


Una variable calculada es aquella que devuelve un valor calculado cada vez que se accede a ella. Es decir, no almacena un valor. Internamente se implementa como una función.

¿Cuál es la diferencia con una función?

  • Semánticamente, una variable es estado, una función es una acción.
  • Una función regula el acceso al almacenamiento privado. Una variable calculada puede hacer lo mismo de una manera más compacta. Ejemplo .
  • Una variable calculada se puede usar con KVO, pasar como #keypath, y tiene facilidades para observar: willSet, didSet.

Deberías usar una variable cuando

  • no tira
  • devuelve una propiedad simple
  • no tiene un efecto secundario o un verbo en su nombre
  • es O (1), es decir, no incurre en un costo significativo. En su ejemplo será O (n).
  • Es idempotente. Múltiples invocaciones idénticas devuelven el mismo valor o establecen el objeto en el mismo estado.

Razones irrelevantes para preferir una variable sobre una función

  • Una variable calculada le evita escribir (). Sin embargo, la claridad es más importante que la brevedad, por lo que este es un argumento débil.
  • Una variable de solo lectura se puede anular como lectura / escritura. Una función indica que siempre es de solo lectura. Sin embargo, Apple usa propiedades para variables de solo lectura como array.count. En caso de duda, busque la coherencia con la plataforma.

Recursos

De  WWDC 2014 - 204 Novedades en Cocoa  > 24:40 Cuándo usar una propiedad @

Use la propiedad para cualquier cosa que se trate del valor o estado de un objeto o su relación con otros objetos. Malos candidatos:

  • Métodos que hacen cosas: cargar, analizar, alternar, ... Tienen verbos en su nombre.
  • Generadores: init, copy, enumerados, ... Estos métodos no son idempotentes.
  • Métodos que cambian de estado: nextObject.

From  Swift Style de Erica Sadun  > Propiedades computadas versus métodos

Una propiedad expresa una cualidad inherente de una instancia, mientras que un método realiza una acción.

  • Los métodos tienen parámetros; propiedades no. Prefiere métodos para cualquier llamada con efectos secundarios. Si un método hace algo (por ejemplo, carga, analiza, alterna o imprime) o tiene un nombre de verbo, no debería ser una propiedad.
  • Prefiere propiedades para valores simples que puedes obtener y / o establecer.
  • Las propiedades deben expresar una calidad intrínseca semántica de una instancia de tipo.
  • Las propiedades le permiten agregar observadores a través de willSet y didSet. A diferencia de las propiedades de instancia almacenadas, las propiedades de tipo almacenado siempre deben tener un valor predeterminado.

Desde  Kotlin convenciones de codificación> funciones vs propiedades . Ver la respuesta de Daniel arriba .

Otros recursos sin información relevante:

Jano
fuente
3

Yo usaría a func. La programación orientada a objetos funciona bien sin propiedades calculadas. Debido a que está recuperando un valor calculado / filtrado, algunos pueden argumentar que una propiedad calculada se siente bien. Pero aquí está mi queja, si haces eso, la legibilidad se ve afectada, porque se siente como un valor.

En este contexto, no tendría sentido intentar asignar el valor calculado (y afortunadamente, el IDE nos ayuda a evitarlo), pero ¿qué sucede si intento asignar algo que se calcula pero que parece un valor?

event.countOfAttendees = 0; // not possible

Al usar func, la persona que llama sabe que no está tratando con un valor directamente:

event.countOfAttendees()

Creo que si se trata de un objeto de comportamiento, debería verse como si se comportara en lugar de verse como una estructura de datos. Si su objeto es tonto y no tiene ningún comportamiento, ¿por qué intentar encapsularlo? En ese caso, también podría hacer que los asistentes sean públicos.

morbidhawk
fuente