web-dev-qa-db-fra.com

Propriété vs en lecture calculée vs Swift

Dans la session d'introduction à Swift WWDC, une propriété en lecture seule description est présentée:

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheels"
    }
}

let vehicle = Vehicle()
println(vehicle.description)

Y a-t-il des implications à choisir l'approche ci-dessus plutôt que d'utiliser une méthode:

class Vehicle {
    var numberOfWheels = 0
    func description() -> String {
        return "\(numberOfWheels) wheels"
    }
}

let vehicle = Vehicle()
println(vehicle.description())

Il me semble que les raisons les plus évidentes pour lesquelles vous choisiriez une propriété calculée en lecture seule sont les suivantes:

  • Sémantique - Dans cet exemple, il est logique que description soit une propriété de la classe plutôt qu'une action effectuée par celle-ci.
  • concision/clarté - évite d'avoir à utiliser des parenthèses vides lors de l'obtention de la valeur.

Il est clair que l’exemple ci-dessus est trop simple, mais existe-t-il d’autres bonnes raisons de choisir l’une plutôt que l’autre? Par exemple, y a-t-il des caractéristiques de fonctions ou des propriétés qui pourraient vous aider à choisir?


N.B. À première vue, cela semble être une question courante OOP, mais je souhaite connaître les fonctionnalités spécifiques à Swift qui guideraient les meilleures pratiques lors de l’utilisation de cette langue.

95
Stuart

Il me semble que c’est surtout une question de style: je préfère fortement utiliser propriétés pour cela: propriétés; signifiant des valeurs simples que vous pouvez obtenir et/ou définir. J'utilise fonctions (ou méthodes) lorsque le travail est en cours. Peut-être que quelque chose doit être calculé ou lu à partir du disque ou d'une base de données: Dans ce cas, j'utilise une fonction, même lorsqu'une valeur simple est renvoyée. Ainsi, je peux facilement voir si un appel est bon marché (propriétés) ou éventuellement cher (fonctions).

Nous aurons probablement plus de clarté lorsque Apple publiera certaines Swift.

51

Eh bien, vous pouvez appliquer les conseils de Kotlin https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties .

Dans certains cas, les fonctions sans argument peuvent être interchangeables avec des propriétés en lecture seule. Bien que la sémantique soit similaire, il existe certaines conventions stylistiques sur le moment de préférer l'une à l'autre.

Préférez une propriété à une fonction lorsque l'algorithme sous-jacent:

  • ne jette pas
  • la complexité est peu coûteuse à calculer (ou à prendre en compte lors du premier passage)
  • renvoie le même résultat sur les invocations
12
onmyway133

Bien que la question des propriétés calculées par rapport aux méthodes en général soit difficile et subjective, il existe actuellement un argument important dans le cas de Swift pour préférer les méthodes aux propriétés. Vous pouvez utiliser des méthodes dans Swift comme fonctions pures, ce qui n’est pas le cas pour les propriétés (à partir de Swift 2.0 bêta). Cela rend les méthodes beaucoup plus puissantes et utiles, car ils peuvent participer à la composition fonctionnelle.

func fflat<A, R>(f: (A) -> () -> (R)) -> (A) -> (R) {
    return { f($0)() }
}

func fnot<A>(f: (A) -> Bool) -> (A) -> (Bool) {
    return { !f($0) }
}

extension String {
    func isEmptyAsFunc() -> Bool {
        return isEmpty
    }
}

let strings = ["Hello", "", "world"]

strings.filter(fnot(fflat(String.isEmptyAsFunc)))
11
Max O

Il y a une différence: si vous utilisez une propriété, vous pouvez éventuellement la remplacer et la rendre en lecture/écriture dans une sous-classe.

7
Analog File

Comme le temps d’exécution est identique, cette question s’applique également à Objective-C. Je dirais, avec les propriétés que vous obtenez

  • une possibilité d'ajouter un séparateur dans une sous-classe, rendant la propriété readwrite
  • une capacité à utiliser KVO/didSet pour les notifications de modification
  • plus généralement, vous pouvez transmettre la propriété aux méthodes qui attendent des chemins de clé, par exemple chercher le tri des demandes

En ce qui concerne quelque chose de spécifique à Swift, le seul exemple que j'ai est que vous pouvez utiliser @lazy pour une propriété.

7
ilya n.

Dans le cas en lecture seule, une propriété calculée doit pas être considérée comme sémantiquement équivalente à une méthode, même si leur comportement est identique, car la suppression de la déclaration func brouille la distinction entre les quantités qui comprennent l'état d'une instance et de quantités qui ne sont que des fonctions de l'état . Vous enregistrez en tapant () sur le site d’appel, mais risquez de perdre la clarté de votre code.

Comme exemple trivial, considérons le type de vecteur suivant:

struct Vector {
    let x, y: Double
    func length() -> Double {
        return sqrt(x*x + y*y)
    }
}

En déclarant la longueur en tant que méthode, il est clair que cela dépend de l’état, qui ne dépend que de x et de y.

D'autre part, si vous deviez exprimer length en tant que propriété calculée

struct VectorWithLengthAsProperty {
    let x, y: Double
    var length: Double {
        return sqrt(x*x + y*y)
    }
}

alors, lorsque vous complétez la page IDE sur une instance de VectorWithLengthAsProperty, vous aurez l’impression que x, y, length étaient des propriétés sur un pied d'égalité, ce qui est conceptuellement incorrect.

4
egnha

Il existe des situations dans lesquelles vous préféreriez une propriété calculée à des fonctions normales. Tels que: renvoyer le nom complet d'une personne. Vous connaissez déjà le prénom et le nom de famille. Donc, vraiment, la propriété fullName est une propriété et non une fonction. Dans ce cas, il s'agit d'une propriété calculée (car vous ne pouvez pas définir le nom complet, vous pouvez simplement l'extraire à l'aide du prénom et du nom).

class Person{
    let firstName: String
    let lastName: String
    init(firstName: String, lastName: String){
        self.firstName = firstName
        self.lastName = lastName
    }
    var fullName :String{
        return firstName+" "+lastName
    }
}
let william = Person(firstName: "William", lastName: "Kinaan")
william.fullName //William Kinaan
2
William Kinaan

Du point de vue de la performance, il ne semble pas y avoir de différence. Comme vous pouvez le voir dans le résultat de référence.

Gist

main.Swift extrait de code:

import Foundation

class MyClass {
    var prop: Int {
        return 88
    }

    func foo() -> Int {
        return 88
    }
}

func test(times: u_long) {
    func testProp(times: u_long) -> TimeInterval {
        let myClass = MyClass()
        let starting = Date()
        for _ in 0...times {
            _ = myClass.prop
        }
        let ending = Date()
        return ending.timeIntervalSince(starting)
    }


    func testFunc(times: u_long) -> TimeInterval {
        let myClass = MyClass()
        let starting = Date()
        for _ in 0...times {
            _ = myClass.prop
        }
        let ending = Date()
        return ending.timeIntervalSince(starting)
    }

    print("prop: \(testProp(times: times))")
    print("func: \(testFunc(times: times))")
}

test(times: 100000)
test(times: 1000000)
test(times: 10000000)
test(times: 100000000)

Sortie:

prop: 0.0380070209503174 func: 0.0350250005722046 prop: 0.371925950050354 func: 0.363085985183716 prop: 3.4023300409317 func: 3.38373708724976 prop: 33.5842199325562 func: 34.8433820009232 Program ended with exit code: 0

Dans le graphique:

benchmark

1
Mint

Sémantiquement, les propriétés calculées doivent être étroitement associées à l'état intrinsèque de l'objet. Si les autres propriétés ne changent pas, l'interrogation de la propriété calculée à des moments différents doit donner le même résultat (comparable via == ou ===) - similaire. d'appeler une fonction pure sur cet objet.

Les méthodes, en revanche, sortent des sentiers battus en partant du principe que nous n'obtiendrons pas toujours les mêmes résultats, car Swift ne permet pas de marquer les fonctions comme étant pures. En outre, les méthodes dans OOP sont des actions considérées, ce qui signifie que leur exécution peut entraîner des effets secondaires. Si la méthode n'a aucun effet secondaire, elle peut être convertie en toute sécurité en propriété calculée.

Notez que les deux instructions ci-dessus sont purement d'un point de vue sémantique, car il se peut que les propriétés calculées aient des effets secondaires inattendus et que les méthodes soient pures.

1
Cristik

Historiquement, la description est une propriété de NSObject et nombreux sont ceux qui s’attendent à ce qu’il en soit de même dans Swift. L'ajout de parens après ne fera qu'ajouter à la confusion.

EDIT: Après un vote à la baisse furieux, je dois clarifier quelque chose - si l’on y accède via la syntaxe à points, cela peut être considéré comme une propriété. Peu importe ce qu'il y a sous le capot. Vous ne pouvez pas accéder aux méthodes habituelles avec la syntaxe à points.

De plus, appeler cette propriété ne nécessitait pas de parenthèses supplémentaires, comme dans le cas de Swift, ce qui peut prêter à confusion.

0
Dvole