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:
description
soit une propriété de la classe plutôt qu'une action effectuée par celle-ci.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.
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.
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
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)))
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.
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
readwrite
didSet
pour les notifications de modificationEn 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é.
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.
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
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.
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:
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.
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.