[Swift] Protocole: renvoyer self ou le type du protocole ?
Jérémy
Membre
Puisque nous sommes dans les bonnes pratiques, j'aurais une petite question.
protocol TotoProtocol {
var name: String { get }
func myFunc()-> TotoProtocol
}
struct Toto: TotoProtocol {
let name: String
init(toto: String) {
self.toto = toto
}
func myFunc()-> TotoProtocol {
return self
}
}
VS
protocol TotoProtocol {
var name: String { get }
func myFunc()-> Self
}
struct Toto: TotoProtocol {
let name: String
init(toto: String) {
self.toto = toto
}
func myFunc()-> Self {
return self
}
}
Ma question est simple. Vaut il mieux privilégié (tant que possible) un retour du type TotoProtocol ou de son implémentation Toto ?
[Edit] : correction du code
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Effectivement j'ai fait une petite boulette. Je viens d'apporter les modifications. ::)
En général je mets le retour du Type mais ça dépend de l'architecture du code et de ce qu'on souhaite faire. Je pense pas qu'il y ait de bonnes pratiques ici. C'est surtout dépendant du besoin selon moi.
Si tu mets TotoProtocol (ex. 1), alors il me semble que le compilateur voit que la fonction renvoie un objet générique conforme à TotoProtocol. Ceci te permet de l'utiliser dans tout ce qui requiert un TotoProtocol.
L'inconvénient, est que tu ne peux pas l'utiliser (sans caster) dans ce qui requiert un Toto. Donc, il me semble qu'il vaut mieux typer en Self (ex. 2): ça fonctionne ainsi dans les deux cas, que ça requiert un Toto ou un TotoProtocol.
C'est pour cette raison que je demande, en paramètre, un objet qui implémente le protocole.
ça compile ça? TotoProtocol n'a pas de propriété nickname; il me semble que la fonction devrait exiger nécessairement un paramètre de type Lala.
Arf... Je te présente mes excuses, je n'avais pas vu que printNicknameOfToto ne faisait pas parti du protocol TotoProtocol.
Avec la fonction myFunc, la différence entre renvoyant TotoProtocol et Self :
Avec TotoProtocol, on renvoie une instance de Toto mais elle est tenue dans une référence de type TotoProtocol ; du coup, tous les membres de Toto sont cachés, sauf ceux qui implement les membres de TotoProtocol.
Avec Self, on renvoie une instance de Toto, tenue dans une référence de Toto ; du coup, on peut accéder tous ses membres.
Quand même, avec Self, on pourrait faire :
Dans ce cas là , t2 est bien assigné une instance de Toto mais, comme avec la première version, les membres de Toto sont cachés.
Tu pourrais arriver à accéder à la variable mais tu es tout de même obligé de faire un cast (tu peux rencontrer la même problématique en Java quand tu travailles avec les Interfaces).
Idem pour l'exemple de Céroce :
Je n'ai pas testé mais je pense que ça passe. ::)
D'une manière geÌneÌrale avec un protocol toujours speÌcifier Self quand on renvoie une instance du type qui impleÌmente le-dit protocol.
ça eÌvite de devoir caster et ça eÌvite aussi d'avoir des surprises en cas d'associatedType dans le protocol.
Si on implémente le protocol dans une struct, on doit retourner le type de la structure:
Alors que si on l'implémente dans une classe, c'est l'inverse, on doit retourner 'Self':
Le problème avec le dernier cas, c'est que si on dérive la classe Titi en Tata, le protocol aura déjà été implémenté par Titi mais Titi aura renvoyé un objet de type Titi, et donc le protocol qui retourne Self ne sera pas correctement implémenté dans Tata, puisque de part sa classe mère, elle renvoie un objet de type Titi.
Retourner Self dans un protocol est un véritable casse-tête si on utilise des classes (cf ici en anglais:http://stackoverflow.com/questions/25645090/protocol-func-returning-self)donc ce type de protocol est à proscrire sauf si on est certain d'utiliser des structs.
Mais dans ce cas c'est un peu contre l'esprit des protocols qui devraient pouvoir être implémentés par n'importe quel type d'objet (y compris une classe).
+1 Pyroh
Dans le code ci dessus, on peut voir que si nous retournont Self (dans notre cas Lala), l'objet retourné pourra très bien passé en paramètre dont les types Lala ou TotoProtocol seront demandés.
Pour ceux qui veulent faire des essais : (désolé pour l'indentation sauvage, j'ai fait ça sur le repl d'IBM).
Je ne peux pas tester actuellement (vivement que playground arrive sur iPad)... :P
Tu es sur de ton coup FKDEV ?
Quel intérêt y aurait il de retourner Self si les objets retournés sont forcément du type Tata ?
Par exemple si on exécute le code suivant :
La raison que l'on utilise Self ; c'est lorsqu'on se trouve dans une hiérarchie des classes où le type pourrait être changé selon la classe/sous-classe ; ou pour un protocol où le type qui l'implémente pourrait varier.
Donc, Self - implémentation dynamique ; Type d'implémenteur - implémentation statique
De manière générale, non, surtout pas.
Si on compte n'implémenter le protocole qu'avec des struct (ou des final class), oui.
Mais ce n'est pas "général" puisque c'est limité aux struct.
Attention, pour gagner un cast en sortie, vous vous interdisez l'utilisation d'une hiérarchie de classes derrière le protocol, ce qui est quand même assez restrictif.
Pour que cela fonctionne dans les classes, il faudrait que le compilateur oblige à implémenter la fonction à chaque niveau de la hiérarchie.
Pour l'instant le compilateur n'accepte la fonction que si on renvoie self. Dans ce cas je ne vois pas trop l'utilité de la fonction.
Je ne maà®trise pas bien les associatedType, peux-tu détailler ?
L'associatedType fonctionne comme les paramètres de type dans un type générique. Ils prennent la place d'un type pas encore connu dans les vars et funcs.
Le type de renvoie que l'on définie sur une méthode dans un protocol variera selon si on utilise un associatedType dans le protocol ou non.
Pour l'implémentation avec une struct, on ne peut qu'utiliser le type de la struct pour type de renvoie
Avec les classes, c'est plus compliqué :
Avec une classe non-final, on est obligé d'utiliser Self pour le type de renvoie, et en plus...
ça prend en considération que le type puisse varier selon les sous-classes disponibles
Mais, avec une classe final, on peut utiliser le type de la classe comme type de renvoie car il est bien connu et ne peut pas changer.
C'est également permis d'utiliser Self pour l'implémentation mais il faut utiliser le dynamicType pour l'instancier, mais sans besoin d'un required init() sur la classe :
Si on insistait en voulant une méthode creator dans le protocol TestProtocol avec un associatedType qui renverrait TestProtocol, c'est beaucoup plus compliqué... mais c'est possible
En revanche, l'implémentation dans une struct est un peu plus moche, comprenant la mort éventuelle d'un poney ::)
Dans ce cas là , moi, je resterai avec Self
Je pense que c'est un peu trop compliqué.
En tous cas pour moi.
Même pour moi. Si tu as plus de questions, poses les et j'essayerai de répondre.