Méthodes de commodité + MVC + Accesseurs

muqaddarmuqaddar Administrateur
septembre 2009 modifié dans Objective-C, Swift, C, C++ #1
Pour mon application, j'aimerais avoir votre avis sur mes modèles.

J'ai pour habitude de remplir mes variables d'instances par un constructeur de commodité.

Actuellement, je récupère du SQL, et j'ai donc créé ce constructeur :

- (id)initWithPrimaryKey:(NSUInteger)anId rows:(FMResultSet*)dbRows&nbsp; {<br />	if (self = [super init]) {<br />		<br />		self._id = anId;<br />		<br />		self._name = [[dbRows stringForColumn:@&quot;name&quot;] retain];	<br /><br />	}<br />	return self;<br />}


Or, FMResultSet est un objet (hérité de NSObject)  qui contient les données SQL (il a donc été rempli par une reqûete SQL mais je n'ai pas moyen de le remplir sans requête apparemment). C'est parfait pour charger ce qui existe déjà  dans la base, mais pas pour créer une instance en vue d'une insertion dans la base (car apparemment ce FMResultSet n'a pas de setters sur les données de son contenu).

J'ai donc créé un autre constructeur de commodité pour charger ce qui provient d'un formulaire avant un nouvel enregistrement.

- (id)initWithPrimaryKey:(NSUInteger)anId dict:(NSDictionary*)aDict {<br />	if (self = [super init]) {<br />		<br />		self._id = anId;<br />		<br />		self._name = [dict objectForKey:@&quot;name&quot;];<br />	}<br />	return self;	<br />}


Ici, on a un dictionnaire qui sera rempli d'objets provenant du formulaire. Ces objets seront chargés dans les iVars de la classe.

1) Est-ce une hérésie d'avoir 2 constructeurs de commodités en MVC ?

2) Je pourrais faire un constructeur avec la liste de toute mes variables (dans le cas du deuxième), mais j'en ai 15 donc je préfère envoyer un dico qui contient tout...

3) Bien sur, je ne pourrais avoir qu'un constructeur avec les 2 arguments FMResultSet ou NSDictionnary et tester nil dessus, mais n'est ce pas moins propre ?

Réponses

  • AliGatorAliGator Membre, Modérateur
    20:27 modifié #2
    Ce n'est pas une hérésie du tout d'avoir plusieurs constructeurs de commodité, d'ailleurs Apple est loin de s'en priver !
    stringWithFormat, stringWithContentsOfFile, stringWithString, stringWithCString:encoding: ... tu crois que c'est quoi tout ça ? :D

    Non pour ma part ça ne me choque pas trop comme design... Le seul truc qui pourrait potentiellement paraà®tre gênant c'est de passer un NSDictionary contenant tes valeurs de variables... et encore que c'est acceptable quand même.
    L'autre solution est de faire des accesseurs pour chacunes de tes variables (plutôt que de les passer lors de la construction, tu les passes une par une juste après ton appel au constructeur). Mais bon ça veux dire qu'alors tu pourras les modifier après la construction, alors que tu veux peut-être éviter que ça puisse être le cas.
  • muqaddarmuqaddar Administrateur
    20:27 modifié #3
    dans 1252419218:

    Ce n'est pas une hérésie du tout d'avoir plusieurs constructeurs de commodité, d'ailleurs Apple est loin de s'en priver !
    stringWithFormat, stringWithContentsOfFile, stringWithString, stringWithCString:encoding: ... tu crois que c'est quoi tout ça ? :D

    Non pour ma part ça ne me choque pas trop comme design... Le seul truc qui pourrait potentiellement paraà®tre gênant c'est de passer un NSDictionary contenant tes valeurs de variables... et encore que c'est acceptable quand même.
    L'autre solution est de faire des accesseurs pour chacunes de tes variables (plutôt que de les passer lors de la construction, tu les passes une par une juste après ton appel au constructeur). Mais bon ça veux dire qu'alors tu pourras les modifier après la construction, alors que tu veux peut-être éviter que ça puisse être le cas.


    C'est vrai que j'y ai pensé de passer chaque variable... Ainsi j'aurais un "alloc, init" générique puis des appels aux setters comme tu dis. Pourquoi pas.

    Il est possible que je veuille les modifier après la construction en tout cas.

    Merci Ali.
  • zoczoc Membre
    20:27 modifié #4
    dans 1252414311:

    Pour mon application, j'aimerais avoir votre avis sur mes modèles.

    J'ai pour habitude de remplir mes variables d'instances par un constructeur de commodité.

    Actuellement, je récupère du SQL, et j'ai donc créé ce constructeur :

    <SNIP>

    J'ai donc créé un autre constructeur de commodité pour charger ce qui provient d'un formulaire avant un nouvel enregistrement.

    Euuh les 2 bouts de code que tu nous a copiés ne sont pas des constructeurs de comodité, mais des méthodes d'initialisation d'instance "normales".  ???

    Les constructeurs de commodité sont des méthodes de classe qui commencent par n'importe quoi sauf alloc, init et copy.
  • AliGatorAliGator Membre, Modérateur
    20:27 modifié #5
    Ah oui tiens exact, comme j'ai lu "méthodes de commodité", quand j'ai lu son code j'ai limite lu comme si c'était le nom de la classe à  la place du "init" pour ma part :P (alors que c'est débile de ma part puisque l'implémentation du code se fait dans les init..., les constructeurs de commodités trucWithMachin n'ayant qu'à  appeler alloc+initWithMachin+autorelease donc tiennent en une ligne)...

    Mais bon, ça ne change pas le problème que c'est tout à  fait acceptable d'avoir plusieurs constructeurs (ou plutôt dirons nous plusieurs initialiseurs). Du moment que tu n'en n'appelles qu'une seul, et qu'une fois
    (je dis ça parce que j'ai récupéré un projet là  où dans le code d'un initWithTruc il y a un appel à  [super initWithTruc] + un [self init] (qui est pourtant déjà  fait dans le super), ça m'a foutu bien la zone ^^)
  • muqaddarmuqaddar Administrateur
    septembre 2009 modifié #6
    Pardonnez-moi, je me suis planté dans mon vocabulaire.

    Voilà  donc au final un vrai constructeur (convenience method) et sa méthode d'initialisation :

    + (id)productorWithPrimaryKey:(NSUInteger)anId rows:(FMResultSet*)dbRows {<br />	return [[[self alloc] initWithPrimaryKey:(NSUInteger)anId rows:(FMResultSet*)dbRows] autorelease];<br />}<br /><br />- (id)initWithPrimaryKey:(NSUInteger)anId rows:(FMResultSet*)dbRows {<br />	if (self = [super init]) {<br />		self._id = anId;<br />		self._countryId = [dbRows intForColumn:@&quot;country_id&quot;];<br />		self._name = [[dbRows stringForColumn:@&quot;name&quot;] retain];	<br />	}<br />	return self;<br />}
    


    Pendant que j'y suis :

    self._countryId = [dbRows intForColumn:@&quot;country_id&quot;];
    


    ou

    _countryId = [dbRows intForColumn:@&quot;country_id&quot;];
    


    ça change quelque chose à  la compil ou en consommation processeur/mémoire ?
  • AliGatorAliGator Membre, Modérateur
    20:27 modifié #7
    Oui ça change énormément, ça change tout en fait.

    [tt]_countryId = xx[/tt] ça affecte xx à  la variable (locale ou d'instance quelle qu'elle soit) _countryId. Juste une bête affectation comme a=5

    [tt]self._countryID = xx[/tt] est traduit par le compilo de la même manière que [tt][self setCountryID:xx][/tt], donc appelle la méthode de setter de la variable. Méthode qui est en réalité souvent autogénérée via le @synthesize, en fait.

    Or ce setter est loin de faire une bête affectation (sauf s'il est de type "nonatomic, assign" à  la limite), puisqu'il gère le multithreading (sauf si tu mets "nonatomic" en définissant la @property) avec des @synchronize automatiquement, génère les événements de KVO (will/didChangeValue:forKey: ) mais aussi et surtout gère la mémoire (retain du nouvel objet / release de l'ancienne valeur).
  • muqaddarmuqaddar Administrateur
    20:27 modifié #8
    dans 1252425208:

    Oui ça change énormément, ça change tout en fait.

    [tt]_countryId = xx[/tt] ça affecte xx à  la variable (locale ou d'instance quelle qu'elle soit) _countryId. Juste une bête affectation comme a=5

    [tt]self._countryID = xx[/tt] est traduit par le compilo de la même manière que [tt][self setCountryID:xx][/tt], donc appelle la méthode de setter de la variable. Méthode qui est en réalité souvent autogénérée via le @synthesize, en fait.

    Or ce setter est loin de faire une bête affectation (sauf s'il est de type "nonatomic, assign" à  la limite), puisqu'il gère le multithreading (sauf si tu mets "nonatomic" en définissant la @property) avec des @synchronize automatiquement, génère les événements de KVO (will/didChangeValue:forKey: ) mais aussi et surtout gère la mémoire (retain du nouvel objet / release de l'ancienne valeur).


    Génial, merci de ta réponse.
    J'ai bien compris.

    Donc c'est très utile dans mon cas.
Connectez-vous ou Inscrivez-vous pour répondre.