J'ai reçu une question intéressante (sous la forme d'un tweet) aujourd'hui sur le chapitre 9. Un lecteur attentif a demandé s'il y avait un moyen de "sec" (Ne vous répétez pas) le code où nous ajoutons tous les contrôleurs de la gamme qui anime la table du contrôleur de vue racine, ce code ici:
C'est un bon endroit. C'est, en fait, un candidat de choix pour le refactoring. Remarquez comment similaires tous les morceaux de code sont. À l'exception du nom de classe du contrôleur, le titre et l'image, chaque morceau de code est fondamentalement identique.
La réponse à savoir si cela peut être DRY'ed, oui. Ce peut être remaniée en Objective-C et devrait probablement. Nous n'avons pas le faire dans le livre essentiellement parce que le chapitre 9 est déjà assez long sans avoir à utiliser des objets de classe ou le runtime Objective-C, et nous étions inquiets ce serait ajouter quelque chose déroutante pour un chapitre déjà long et difficile.
Mais, mon blog ne doit pas être seulement amicale débutants, nous allons donc voir comment nous pourrions refactoriser ce bout de code. Tout d'abord, nous allons commencer par modifier la propriété des contrôleurs à partir d'un NSArray à un NSMutableArray et son contenu peut être modifié par une méthode d'instance.
Ensuite, nous pouvons créer une méthode qui va ajouter un contrôleur à ce tableau. Depuis les articles qui ne sont pas les mêmes entre les différents morceaux de code sont la classe du contrôleur, le titre et le nom de l'image, nous avons besoin de la méthode de prendre les arguments de chacun de ces.
Si nous savons et d'avoir accès au moment de la compilation de toutes les classes que nous allons utiliser, nous pouvons le faire assez facilement en créant une méthode qui prend un objet de classe. C'est l'objet qui représente le singleton méta-objet qui existe pour toutes les classes Objective-C. Lorsque vous appelez une méthode de classe, vous êtes réellement appeler une méthode sur cet objet et vous pouvez appeler des méthodes sur des objets de classe de classe. Alors, dans ce scénario où nous savons que toutes les classes que nous allons utiliser, nous pouvons écrire cette méthode:
Nous créons une instance de la classe correcte en appelant alloc sur l'objet de classe, ce qui renvoie une instance, qui nous permettra ensuite d'initialiser normalement. Nous déclarons ce une instance d'être la superclasse abstraite de tous les contrôleurs de deuxième niveau, SecondLevelViewController, qui nous permet d'utiliser le titre et propriétés rowImage sans avoir à transtyper ou définir les valeurs par clé.
Ensuite, notre méthode viewDidLoad devient beaucoup, beaucoup plus courte et sans tout le code répété:
Mais, si vous ne connaissez pas toutes les classes au moment de la compilation? Dites, si vous voulez créer une classe générique d'aller dans une librairie statique? Vous pouvez toujours le faire, mais vous perdez le contrôle de la compilation de la classe et doivent utiliser une méthode d'exécution d'Objective-C pour dériver un objet de classe du nom de la classe. Assez facile, cependant. Selon ce scénario, voici notre nouvelle méthode:
Notez que la seule différence est que nous prenons un paramètre * NSString plutôt que d'un paramètre de classe, et puis on récupère l'objet de classe correcte en utilisant l'Objective-C runtime fonction appelée objc_getClass (). Cette fonction prend en fait un C-string, pas une NSString, alors nous obtenons un C-string en utilisant la méthode d'instance sur notre UTF8String NSString.
Dans ce cas, nous devons changer notre méthode viewDidLoad légèrement pour passer des constantes de chaîne, plutôt que des objets de classe:
Chacune de ces options sera beaucoup plus facile à maintenir et à étendre que la version dans le livre. Vous devriez être à l'affût des opportunités de refactoring dans votre propre code, aussi bien. Parfois, une once de refactorisation peut sauver une livre de maux de tête sur la ligne.
- (void)viewDidLoad {
self.title = @"First Level";
NSMutableArray *array = [[NSMutableArray alloc] init];
// Disclosure Button
DisclosureButtonController *disclosureButtonController =
[[DisclosureButtonController alloc]
initWithStyle:UITableViewStylePlain];
disclosureButtonController.title = @"Disclosure Buttons";
disclosureButtonController.rowImage = [UIImage
imageNamed:@"disclosureButtonControllerIcon.png"];
[array addObject:disclosureButtonController];
[disclosureButtonController release];
// Check List
CheckListController *checkListController = [[CheckListController alloc]
initWithStyle:UITableViewStylePlain];
checkListController.title = @"Check One";
checkListController.rowImage = [UIImage imageNamed:
@"checkmarkControllerIcon.png"];
[array addObject:checkListController];
[checkListController release];
// Table Row Controls
RowControlsController *rowControlsController =
[[RowControlsController alloc]
initWithStyle:UITableViewStylePlain];
rowControlsController.title = @"Row Controls";
rowControlsController.rowImage = [UIImage imageNamed:
@"rowControlsIcon.png"];
[array addObject:rowControlsController];
[rowControlsController release];
// Move Me
MoveMeController *moveMeController = [[MoveMeController alloc]
initWithStyle:UITableViewStylePlain];
moveMeController.title = @"Move Me";
moveMeController.rowImage = [UIImage imageNamed:@"moveMeIcon.png"];
[array addObject:moveMeController];
[moveMeController release];
// Delete Me
DeleteMeController *deleteMeController = [[DeleteMeController alloc]
initWithStyle:UITableViewStylePlain];
deleteMeController.title = @"Delete Me";
deleteMeController.rowImage = [UIImage imageNamed:@"deleteMeIcon.png"];
[array addObject:deleteMeController];
[deleteMeController release];
// President View/Edit
PresidentsViewController *presidentsViewController =
[[PresidentsViewController alloc]
initWithStyle:UITableViewStylePlain];
presidentsViewController.title = @"Detail Edit";
presidentsViewController.rowImage = [UIImage imageNamed:
@"detailEditIcon.png"];
[array addObject:presidentsViewController];
[presidentsViewController release];
self.controllers = array;
[array release];
[super viewDidLoad];
}C'est un bon endroit. C'est, en fait, un candidat de choix pour le refactoring. Remarquez comment similaires tous les morceaux de code sont. À l'exception du nom de classe du contrôleur, le titre et l'image, chaque morceau de code est fondamentalement identique.
La réponse à savoir si cela peut être DRY'ed, oui. Ce peut être remaniée en Objective-C et devrait probablement. Nous n'avons pas le faire dans le livre essentiellement parce que le chapitre 9 est déjà assez long sans avoir à utiliser des objets de classe ou le runtime Objective-C, et nous étions inquiets ce serait ajouter quelque chose déroutante pour un chapitre déjà long et difficile.
Mais, mon blog ne doit pas être seulement amicale débutants, nous allons donc voir comment nous pourrions refactoriser ce bout de code. Tout d'abord, nous allons commencer par modifier la propriété des contrôleurs à partir d'un NSArray à un NSMutableArray et son contenu peut être modifié par une méthode d'instance.
#import <Foundation/Foundation.h>
@interface FirstLevelViewController : UITableViewController {
NSMutableArray *controllers;
}
@property (nonatomic, retain) NSMutableArray *controllers;
@end
Ensuite, nous pouvons créer une méthode qui va ajouter un contrôleur à ce tableau. Depuis les articles qui ne sont pas les mêmes entre les différents morceaux de code sont la classe du contrôleur, le titre et le nom de l'image, nous avons besoin de la méthode de prendre les arguments de chacun de ces.
Si nous savons et d'avoir accès au moment de la compilation de toutes les classes que nous allons utiliser, nous pouvons le faire assez facilement en créant une méthode qui prend un objet de classe. C'est l'objet qui représente le singleton méta-objet qui existe pour toutes les classes Objective-C. Lorsque vous appelez une méthode de classe, vous êtes réellement appeler une méthode sur cet objet et vous pouvez appeler des méthodes sur des objets de classe de classe. Alors, dans ce scénario où nous savons que toutes les classes que nous allons utiliser, nous pouvons écrire cette méthode:
- (void)addControllerOfClass:(Class)controllerClass usingTitle:(NSString *)title withImageNamed:(NSString *)imageName {
SecondLevelViewController *controller = [[controllerClass alloc] initWithStyle:UITableViewStylePlain];
controller.title = title;
controller.rowImage = [UIImage imageNamed:imageName];
[self.controllers addObject:controller];
[controller release];
}Nous créons une instance de la classe correcte en appelant alloc sur l'objet de classe, ce qui renvoie une instance, qui nous permettra ensuite d'initialiser normalement. Nous déclarons ce une instance d'être la superclasse abstraite de tous les contrôleurs de deuxième niveau, SecondLevelViewController, qui nous permet d'utiliser le titre et propriétés rowImage sans avoir à transtyper ou définir les valeurs par clé.
Ensuite, notre méthode viewDidLoad devient beaucoup, beaucoup plus courte et sans tout le code répété:
- (void)viewDidLoad {
self.title = @"First Level";
NSMutableArray *array = [[NSMutableArray alloc] init];
self.controllers = array;
[array release];
[self addControllerOfClass:[DisclosureButtonController class] usingTitle:@"Disclosure Buttons" withImageNamed:@"disclosureButtonControllerIcon.png"];
[self addControllerOfClass:[CheckListController class] usingTitle:@"Check One" withImageNamed:@"checkmarkControllerIcon.png"];
[self addControllerOfClass:[RowControlsController class] usingTitle:@"Row Controls" withImageNamed:@"rowControlsIcon.png"];
[self addControllerOfClass:[MoveMeController class] usingTitle:@"Move Me" withImageNamed:@"moveMeIcon.png"];
[self addControllerOfClass:[DeleteMeController class] usingTitle:@"Delete Me" withImageNamed:@"deleteMeIcon.png"];
[self addControllerOfClass:[PresidentsViewController class] usingTitle:@"Detail Edit" withImageNamed:@"detailEditIcon.png"];
[super viewDidLoad];
}Mais, si vous ne connaissez pas toutes les classes au moment de la compilation? Dites, si vous voulez créer une classe générique d'aller dans une librairie statique? Vous pouvez toujours le faire, mais vous perdez le contrôle de la compilation de la classe et doivent utiliser une méthode d'exécution d'Objective-C pour dériver un objet de classe du nom de la classe. Assez facile, cependant. Selon ce scénario, voici notre nouvelle méthode:
- (void)addControllerOfName:(NSString *)controllerClassName usingTitle:(NSString *)title withImageNamed:(NSString *)imageName {
Class controllerClass = objc_getClass([controllerClassName UTF8String]);
SecondLevelViewController *controller = [[controllerClass alloc] initWithStyle:UITableViewStylePlain];
controller.title = title;
controller.rowImage = [UIImage imageNamed:imageName];
[self.controllers addObject:controller];
[controller release];
}Notez que la seule différence est que nous prenons un paramètre * NSString plutôt que d'un paramètre de classe, et puis on récupère l'objet de classe correcte en utilisant l'Objective-C runtime fonction appelée objc_getClass (). Cette fonction prend en fait un C-string, pas une NSString, alors nous obtenons un C-string en utilisant la méthode d'instance sur notre UTF8String NSString.
Dans ce cas, nous devons changer notre méthode viewDidLoad légèrement pour passer des constantes de chaîne, plutôt que des objets de classe:
- (void)viewDidLoad {
self.title = @"First Level";
NSMutableArray *array = [[NSMutableArray alloc] init];
self.controllers = array;
[array release];
[self addControllerOfName:@"DisclosureButtonController" usingTitle:@"Disclosure Buttons" withImageNamed:@"disclosureButtonControllerIcon.png"];
[self addControllerOfName:@"CheckListController" usingTitle:@"Check One" withImageNamed:@"checkmarkControllerIcon.png"];
[self addControllerOfName:@"RowControlsController" usingTitle:@"Row Controls" withImageNamed:@"rowControlsIcon.png"];
[self addControllerOfName:@"MoveMeController" usingTitle:@"Move Me" withImageNamed:@"moveMeIcon.png"];
[self addControllerOfName:@"DeleteMeController" usingTitle:@"Delete Me" withImageNamed:@"deleteMeIcon.png"];
[self addControllerOfName:@"PresidentsViewController" usingTitle:@"Detail Edit" withImageNamed:@"detailEditIcon.png"];
[super viewDidLoad];
}Chacune de ces options sera beaucoup plus facile à maintenir et à étendre que la version dans le livre. Vous devriez être à l'affût des opportunités de refactoring dans votre propre code, aussi bien. Parfois, une once de refactorisation peut sauver une livre de maux de tête sur la ligne.
Aucun commentaire:
Enregistrer un commentaire