1

I would like to create a "multi-level" class cluster such that each "concrete" class could return a more specific instance when certain criteria are met.

For instance in the base class:

@interface BaseClass : NSObject  // this is the public API of the service

+(instancetype)initWithData:(Data *)data;
// common interface of all the specific implementations..
...
...

@end


@implementation BaseClass

// basically implementing the "cluster"
+(instancetype)initWithData:(Data *)data {
  // testing some conditions to decide on the more specific version of the class to return...
  if (data.condition == condition1) {
     return [[SomeClassOne alloc] initWithData:data];
  }
  if(data.condition == condition2) {
     return [[SomeClassTwo alloc] initWithData:data];
  }
  ... 
  // have many types which could be returned 
}


// an example of a specific instance that should be returned from the cluster - all of these classes are "private" implementations of the base class
@implementation SomeClassOne

-(instancetype)initWithData:(Data *)data {
  self = [super initWithData:data];
  // all was good until a new optimization came about...
  // now this instance can refine the class cluster even better
  // what I would want is a way to do:
  self = [[SomeClassOne_EvenBetterVersion alloc] initWithData:data];
  // but this would be bad - and would cause recursion if the new version inherits this version...
}
@end

I don't want to constantly add new conditions in the base class (The big "if" statement) because the conditions become very specific to the concrete classes - mostly optimizations that have to do with new capabilities.

Is there a better pattern for this?

I thought about creating a class method in each sub class that would do additional checks - but this becomes really awkward having to call [Subclass initWithData:data] in each subclass

Avba
  • 14,822
  • 20
  • 92
  • 192

1 Answers1

1

A few things that might improve this:

1) Override allocWithZone: for your BaseClass to return a singleton instance of BaseClass. This prevents extra object allocations. The initWithData: method of the BaseClass will then return you the real instance.

2) Structure or transform the Data parameter in such a way for easy dictionary lookups to get your concrete implementation class. Instantiate that in the initWithData:

Create a structure statically in +initialize (or dynamically whenever) that looks like:

static_dictionary = @{ @"Some string or hashable condition" : [CoolSubclass class],
                       @"Condition2" : [CoolerSubclass class] };

Now in the initWithData: you have constant time look ups for your type and clean code.

Class my_type = [static_dictionary objectForKey: transformated_data_condition];
if(my_type == Nil) { // throw? return nil? }
return [[my_type alloc] initWithData: data];

Now it becomes easy to repeat the process - Maybe CoolerSubclass is another class cluster that has its own dictionary of types and test method.

KirkSpaziani
  • 1,962
  • 12
  • 14
  • I think that zones are no longer used. Regarding using dictionary - that is great, but cascading to the next level requires creating a new -init method to ensure that the subclass doesn't call "super" – Avba Sep 10 '15 at 17:41
  • 1
    alloc calls allocWithZone:, I believe that it's still the preferred method to override for a situation like this. Your subclass init methods should be smart and bypass the initWithData (calling init). – KirkSpaziani Sep 10 '15 at 17:56
  • This method exists for historical reasons; memory zones are no longer used by Objective-C. https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/#//apple_ref/occ/clm/NSObject/allocWithZone: – Avba Sep 10 '15 at 20:34
  • Swizzle it in NSObject and log when it gets called. Its original purpose is no longer relevant but it is still called. Here is a good resource: https://mikeash.com/pyblog/friday-qa-2010-03-12-subclassing-class-clusters.html#comment-ab500132fda92b49f33081ae9d338f5b Also from the NSObject documentation above: For historical reasons, alloc invokes allocWithZone: – KirkSpaziani Sep 10 '15 at 21:33