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
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:
Now in the initWithData: you have constant time look ups for your type and clean code.
Now it becomes easy to repeat the process - Maybe CoolerSubclass is another class cluster that has its own dictionary of types and test method.