1

I want to create classes Car, Vehicle, and Airplane with the following properties:

  • Car and Airplane are both subclasses of Vehicle.
  • Car and Airplane both have an initWithString method.
  • The acceptable input strings for Car's and Airplane's initWithString methods do not overlap.
  • Vehicle is "almost abstract", in the sense that any initialized instance should be either a Car or an Airplane.
  • It is possible to pass a string into Vehicle and get back an instance of Car, an instance of Airplane, or nil, depending on the input string.

Any particular design pattern I should prefer? In particular for Vehicle's initWithString and/or newVehicleWithString methods.

Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
William Jockusch
  • 26,513
  • 49
  • 182
  • 323

2 Answers2

4

What you need is the "class cluster" pattern. Your Vehicle initWithString: method could look something like this:

- (id) initWithString:(NSString *)mode {
  // note that we don't call [super init] in a class cluster. 
  // instead, we have to release self because it's an unwanted Vehicle instance
  [self release];
  if([mode isEqualToString:@"wheels"]) {
    return [[Car alloc] initWithString:@"wheels"];
  }
  if([mode isEqualToString:@"wings"]) {
    return [[Airplane alloc] initWithString:@"wings"];
  }
  return nil;  //alternately, raise NSInvalidArgumentException
}
codewarrior
  • 2,000
  • 14
  • 14
  • I think it's useful to note that in this case it would probably be a lot more efficient to override alloc and return a special singleton instance that allocates and initializes the correct class and returns that. – Jason Coco Mar 30 '10 at 01:13
  • 1
    True, but that sounds a lot like premature optimization. – codewarrior Mar 30 '10 at 01:17
  • It seems these days everyone has to take everything to an extreme. Now people don't think about even simple solutions when there is a very clear and easy opportunity to 'optimize'. In your case you are creating and throwing away objects 100% of the time. We need balance, people :) In this instance it would be cleaner and more correct anyway--alloc is also where class clusters are created, not in init. – Jason Coco Mar 30 '10 at 01:30
  • I agree that not creating a throwaway object is an obvious thing to do. It's also true that Apple's class clusters also work like this, returning a singleton placeholder instance from `[NSDictionary alloc]` et al. But to put it another way, I'm letting the computer throw away all those objects so I won't have to create a singleton placeholder class. – codewarrior Mar 30 '10 at 02:12
  • Well I could have it both ways by not having an initWithString method in vehicle at all . . . instead having a newVehicleWithString method that allocates and initiates the correct subclass. – William Jockusch Mar 30 '10 at 02:52
  • I KNEW we were overthinking this! – codewarrior Mar 30 '10 at 03:24
  • Well, that is an extremely silly reason to be creating and throwing away all those objects, /especially/ on a resource-limited device. Alloc is really the best, most appropriate place to do this. Basically he is creating a factory here and in that pattern the true instance should be created by some singleton (or created directly). – Jason Coco Mar 30 '10 at 03:43
0

Referring to a subclass from a superclass is not a good idea.

If you really have to do this, you should at least go with a class method like vehicleWithString:.

In fact, I doubt that the other approach (using vehicle initWithString: to create instances of Car or Airplane) would work.

Can Berk Güder
  • 109,922
  • 25
  • 130
  • 137
  • 1
    The superclass-referring-to-subclass thing is how class clusters work all throughout Cocoa. – Chuck Mar 29 '10 at 23:03
  • On the contrary! This is a design pattern called "class clusters", where `Vehicle initWithString:` decides which subclass to return an instance of. This pattern is used throughout Cocoa, especially by NSString. – codewarrior Mar 29 '10 at 23:05
  • This is precisely why I love SO, thanks. However, as far as I can tell, the subclasses in class clusters in Cocoa are all private. I still think using this pattern with public subclasses is somewhat dangerous. – Can Berk Güder Mar 30 '10 at 00:44