Objectif C – créer des instances de class concrètes à partir de la class de base en fonction du type

Juste pour donner un exemple réel, disons que la class de base est Vehicle et que les classs concrètes sont TwoWheeler et FourWheeler. Maintenant, le type de véhicule – TwoWheeler ou FourWheeler, est décidé par le véhicule de la class de base. Lorsque je crée une instance de TwoWheeler / FourWheeler en utilisant la méthode alloc-init, elle appelle la super implémentation comme ci-dessous pour définir la valeur des propriétés communes définies dans la class Vehicle et l'une d'elles est le type qui décide si le type est TwoWheeler ou FourWheeler.

if (self = [super initWithDictionary:dict]){ [self setOtherAtsortingbutes:dict]; return self; } 

Maintenant, quand je reçois une collection de véhicules, certains d'entre eux pourraient être TwoWheeler et d'autres seront FourWheeler. Par conséquent je ne peux pas directement créer une instance de TwoWheeler ou FourWheeler comme ceci

 Vehicle *v = [[TwoWheeler alloc] initWithDictionary:dict]; 

Est-il possible de créer une instance de class de base et une fois que je connais le type, créer une instance de class enfant en fonction du type et le renvoyer. Avec l'implémentation actuelle, il en résulterait une boucle infinie car j'appelle une super implémentation depuis une class concrète.

Quel serait le design parfait pour gérer ce scénario quand je ne sais pas quelle class concrète devrait être instanciée à l'avance?

Généralement, cela est fait avec une usine .

Si vous voulez que l'usine fasse partie de la class de base, c'est bien, mais cela pourrait causer des problèmes dans le futur. En Objective C, les methods de class font de bonnes usines.

 + (Vehicle *)vehicleWithDictionary:(NSDictionary *)dict { if ([[dict objectForKey:kVehicleType] isEqualToSsortingng:@"TwoWheeler"]) { return [[[TwoWheeler alloc] initWithDictionary:dict] autorelease]; } else if ([[dict objectForKey:kVehicleType] isEqualToSsortingng @"FourWheeler"]) { return [[[FourWheeler alloc] initWithDictionary:dict] autorelease]; } else { return [[[Vehicle alloc] initWithDictionary:dict] autorelease]; } } 

L'usine peut faire partie de la class Vehicle et être utilisée comme telle.

 // Instead of [[Vehicle alloc] initWithDictionary:dict] Vehicle *vehicle = [Vehicle vehicleWithDictionary:dict]; 

Mettre à jour

Je suis venu avec un moyen de faire ce qu'on m'a demandé. Laissez-le servir comme un exemple shiny de pourquoi c'est une si mauvaise idée et pourquoi vous ne devriez jamais le faire.

 - (id)initWithDictionary:(NSDictionary *)dict { self = [super init]; if (self) { // If override is in the dictionary, then it mustn't try to call the subclass. if (![dict objectForKey:kOverride]) { NSMutableDictionary *overrideDict = [NSMutableDictionary dictionaryWithDictionary:dict]; [overrideDict setObject:@"override" forKey:kOverride]; if ([[dict objectForKey:kVehicleType] isEqualToSsortingng:@"TwoWheeler"]) { [self release]; return [[[TwoWheeler alloc] initWithDictionary:overrideDict] autorelease]; } else if ([[dict objectForKey:kVehicleType] isEqualToSsortingng @"FourWheeler"]) { [self release]; return [[[FourWheeler alloc] initWithDictionary:overrideDict] autorelease]; } } // init as normal } return self; } 

vous devriez utiliser l'usine abstraite Comme suit, la class Vehicle aura une méthode appelée, createInstance, cette méthode aura un paramètre qui décidera quoi créer considérons l'exemple

 + (Vehicle*) createInstance:(int)numberOfWheels { if(numberOfWheels == 2) { return [[TwoWheeler alloc] init]; } else { return [[FourWheeler alloc] init]; } return nil; } 

Vous l'appelleriez comme ça

 Vehicle *v = [Vehicle createInstance:2];