Commutateur Objective-C utilisant des objets?
Je suis en train de faire quelques Objective-C programmation qui implique l'analyse d'un NSXmlDocument et de remplir un de propriétés d'objets à partir du résultat.
Première version ressemblait à ceci:
if([elementName compare:@"companyName"] == 0)
[character setCorporationName:currentElementText];
else if([elementName compare:@"corporationID"] == 0)
[character setCorporationID:currentElementText];
else if([elementName compare:@"name"] == 0)
...
Mais je n'aime pas le if-else-if-else
modèle de ce produit. En regardant les switch
déclaration, je vois que je ne peut gérer que ints
chars
etc et non pas des objets... donc, il y a une meilleure mise en œuvre de patron je ne suis pas au courant?
BTW, je n'ai vraiment trouver une meilleure solution pour paramétrer les propriétés de l'objet, mais je veux savoir précisément de la if
-else
vs switch
modèle en Objective-C
source d'informationauteur craigb
Vous devez vous connecter pour publier un commentaire.
J'espère que vous allez tous me pardonner pour aller sur une branche ici, mais je voudrais aborder la question plus générale de l'analyse des documents XML dans le Cacao, sans la nécessité de if-else. La question avait d'abord déclaré affecte le courant de l'élément de texte à une variable d'instance de l'objet personnage. Comme jmah souligné, cela peut être résolu en utilisant la valeur-clé de codage. Cependant, dans un environnement plus complexe document XML cela pourrait ne pas être possible. Considérons par exemple la suite.
Il existe plusieurs approches pour traiter cette question. Hors du haut de ma tête, je pense à deux à l'aide de NSXMLDocument. La première utilise NSXMLElement. Il est assez simple et n'implique pas l'if-else problème. Vous obtenez simplement l'élément racine et allez par le biais de ses éléments un par un.
La prochaine on utilise le plus général NSXMLNode, des promenades à travers les arbres, et utilise directement les si-le reste de la structure.
C'est un bon candidat pour l'élimination de la si-sinon de la structure, mais comme le problème d'origine, nous ne pouvons pas tout simplement utiliser le commutateur-cas ici. Cependant, on peut toujours éliminer if-else en utilisant performSelector. La première étape est de définir la méthode pour chaque élément.
La magie qui se passe dans le invokeMethodForNode:préfixe: méthode. Nous générons le sélecteur de fonction sur le nom de l'élément, et effectuer cette sélecteur avec aNode comme seul paramètre. Presto bango, nous avons éliminé la nécessité d'un if-else. Voici le code de cette méthode.
Maintenant, au lieu de nos plus grands if-else (celui que la distinction entre la société et corporationID), on peut simplement écrire une seule ligne de code
Maintenant, je m'excuse si je me suis trompe, il a été un moment depuis que j'ai écrit n'importe quoi avec NSXMLDocument, il est tard le soir et je n'ai pas fait de test de ce code. Donc si vous voyez quelque chose de mal, vous pouvez laisser un commentaire ou modifier cette réponse.
Cependant, je crois que j'ai juste montré comment correctement nommées par les sélecteurs peuvent être utilisés dans le Cacao, afin d'éliminer complètement le if-else dans de tels cas. Il y a quelques pièges et coin affaires. Le performSelector: famille de méthodes ne prend que les valeurs 0, 1 ou 2 méthodes argument dont les arguments et les types de retour sont des objets, de sorte que si les types des arguments et le type de retour ne sont pas des objets, ou si il y a plus de deux arguments, alors que vous auriez à utiliser une NSInvocation pour l'appeler. Vous devez vous assurer que les noms de méthode vous générez ne vont pas à appeler d'autres méthodes, en particulier si l'objectif de l'appel est d'un autre objet, et ce mode de schéma d'affectation de noms ne fonctionne pas sur les éléments avec les caractères non-alphanumériques. Vous pourriez contourner en s'échappant de l'élément XML dans les noms de vos noms de méthode en quelque sorte, ou par la construction d'un NSDictionary à l'aide de la méthode des noms comme les clés et les sélecteurs comme les valeurs. Cela peut être assez gourmande en mémoire et finissent par prendre plus de temps. performSelector expédition comme je l'ai décrit est assez rapide. Pour les très grandes if-else, cette méthode peut être même plus vite qu'un if-else.
Vous devriez profiter de la Valeur-Clé de Codage:
Si les données sont peu fiables, vous pourriez vouloir vérifier que la clé est valide:
Si vous souhaitez utiliser aussi peu de code que possible, et vos noms d'élément et les setters sont tous nommés, de sorte que si elementName est @"foo", puis setter est setFoo:, vous pourriez faire quelque chose comme:
ou peut-être même:
Si ces seront bien sûr être un peu plus lent que d'utiliser un tas de if.
[Edit: La deuxième option a déjà été mentionné par quelqu'un; oups!]
Oserais-je suggérer à l'aide d'une macro?
Un sens, je l'ai fait avec NSStrings est à l'aide d'un NSDictionary et les énumérations. Il peut ne pas être la plus élégante, mais je pense que ça rend le code un peu plus lisible. Le pseudo-code suivant est extrait de un de mes projets:
La
if-else
mise en œuvre que vous avez est la bonne façon de le faire, depuisswitch
ne fonctionne pas avec les objets. À part peut-être un peu plus difficile à lire (ce qui est subjectif), il n'y a pas de réel inconvénient dans l'utilisation deif-else
déclarations de cette façon.Bien qu'il n'y a pas nécessairement une meilleure façon de faire quelque chose comme ça pour une utilisation unique, pourquoi utilisez la fonction "comparer" quand vous pouvez utiliser "isEqualToString"? Qui semblerait être plus performant car la comparaison s'arrête à la première non-correspondance de caractère, plutôt que de passer par l'ensemble de la chose à calculer valide résultat de la comparaison (même si à bien y penser, la comparaison peut être clairement au même point) - aussi bien qu'il aurait l'air un peu plus propre, parce que l'appel renvoie un BOOLÉEN.
Il est en fait une manière assez simple de traiter avec des if-else dans une langue comme l'Objective-C. Oui, vous pouvez utiliser le sous-classement et prioritaire, la création d'un groupe de sous-classes qui implémentent la même méthode différemment, en invoquant la bonne mise en œuvre lors de l'exécution à l'aide d'un message commun. Cela fonctionne bien si vous voulez choisir l'un des quelques implémentations, mais il peut entraîner une prolifération inutile des sous-classes si vous avez beaucoup de petits, légèrement différentes implémentations comme vous avez tendance à avoir dans longtemps si l'autre ou les instructions switch.
Au lieu de cela, le facteur le corps de chaque si/d'autre-si la clause dans sa propre méthode, tous dans la même classe. Nom les messages qui l'invoque, de la même façon. Maintenant, créez un NSArray contenant les sélecteurs de ces messages (obtenu à l'aide de @selector()). Contraindre la chaîne que vous testiez dans les instructions conditionnelles dans un sélecteur à l'aide NSSelectorFromString() (vous pouvez avoir besoin de concaténer des mots ou deux points de la première, selon la façon dont vous le nom de ces messages, et si oui ou non ils prennent des arguments). Maintenant j'ai effectuer le sélecteur à l'aide performSelector:.
Cette approche a l'inconvénient qu'il peut l'encombrement de la classe avec de nombreux nouveaux messages, mais c'est probablement mieux à encombrer une classe unique de l'ensemble de la hiérarchie de classe avec de nouvelles sous-classes.
Affichage présente comme une réponse à Wevah la réponse ci-dessus -- je l'ai édité, mais je n'ai pas assez haute réputation encore:
malheureusement la première méthode ne fonctionne que pour les champs avec plus d'un mot en eux-comme xPosition. capitalizedString permettra de convertir Xposition, qui, lorsqu'il est combiné avec le format de vous donner setXposition: . Certainement pas ce qu'on voulait ici. Voici ce que j'utilise dans mon code:
Pas aussi jolie que la première méthode, mais ça marche.
Je suis venu avec une solution qui utilise des blocs pour créer un commutateur de structure pour les objets. Là, il va:
Comme vous pouvez le voir, c'est un oldschool fonction C avec liste d'arguments variable. J'ai passer de l'objet à tester dans le premier argument, suivie par la case_value-case_block paires. (Rappelons que l'Objectif-C, les blocs ne sont que des objets.) Le
while
boucle conserve l'extraction de ces paires jusqu'à ce que la valeur d'un objet est mis en correspondance ou il n'y a aucun cas à gauche (voir les notes ci-dessous).Utilisation:
Notes:
Le plus commun de refactoring proposé pour l'élimination de l'if-else ou des instructions de commutation est l'introduction de polymorphisme (voir http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html). L'élimination de ces conditions est la plus importante quand ils sont dupliqués. Dans le cas de l'analyse XML comme votre échantillon, vous êtes essentiellement à déplacer les données vers un plus à la structure naturelle de sorte que vous n'aurez pas à dupliquer le conditionnel ailleurs. Dans ce cas la si-d'autre ou de l'instruction switch est probablement assez bon.
Dans ce cas, je ne suis pas sûr si vous pouvez facilement refactoriser la classe à introduire polymorphisme comme Bradley suggère, puisque c'est un Cacao natif de classe. Au lieu de cela, l'Objective-C façon de le faire est d'utiliser une catégorie pour ajouter une
elementNameCode
méthode pour NSSting:Dans votre code, vous pouvez maintenant utiliser un interrupteur sur
[elementName elementNameCode]
(et de gagner de l'associé avertissements du compilateur si vous oubliez de tester l'un de l'enum membres, etc.).Comme Bradley points, cela peut ne pas être utile si la logique n'est utilisé que dans un seul endroit.
Ce que nous avons fait dans nos projets où nous devons donc ce genre de chose et plus, est de mettre en place un statique CFDictionary cartographie les chaînes/objets à vérifier par rapport à une simple valeur de type entier. Elle conduit à un code qui ressemble à ceci:
Si vous ciblez Leopard seulement, vous pouvez utiliser un NSMapTable au lieu d'un CFDictionary.
Similaire à Lvsti je suis à l'aide de blocs pour effectuer un commutation de modèle sur les objets.
J'ai écrit un bloc de filtre en fonction de la chaîne, qui prend n de blocs de filtre et effectue chaque filtre sur l'objet.
Chaque filtre peut modifier l'objet, mais il doit le restituer. N'importe quoi.
NSObject+Fonctionnel.h
NSObject+Fonctionnel.m
Maintenant, nous pouvons mettre en place
n
FilterBlocks de test pour les différents cas.Maintenant nous tenir ces bloc, nous voulons tester comme un filtre de la chaîne dans un tableau:
et peut effectuer sur un objet
Cette approche peut être utilisée pour la commutation, mais aussi pour tout (conditionnel) filtre de la chaîne demande, comme les blocs peuvent modifier l'élément et de le transmettre.