views:

155

answers:

3

I have two similar classes, MultiSlotBlock and SingleSlotBlock. They have started to share a lot of common code so I have decided to do some refactoring and pull some of the methods up to a new superclass, let's call it Block.

Now one of the methods that I pull up, simplified for the example, looks like this:

// (Block.mm)

- (void)doACommonBehaviour
{    
      // .. does some stuff

      [self doAUniqueBehaviour];
}

The problem here is that [self doAUniqueBehaviour] is showing a warning because of course my superclass doesn't implement this method anywhere.

The two solutions I thought of don't sound great to me. One is to use a protocol (the way I am currently doing it) like so:

// (Block.mm)

- (void)doACommonBehaviour
{    
      // .. does some stuff

      if ([self conformsToProtocol:@protocol(UniqueBehaviourProtocol)])
      {
           id<UniqueBehaviourProtocol> block = (id<UniqueBehaviourProtocol>)self;
           [block doAUniqueBehaviour];
      }
}

The other is to have a blank method body in my superclass (in this case there would be a lot) and just return doesNotRespondToSelector.

Something is tingling at the back of my mind that I should be using the Strategy Pattern, but I might be way off, and I haven't thought through how that would be implemented.

Any ideas? Thanks.

EDIT: I know for a fact that doAUniqueBehaviour will be implemented in all subclasses, it is just the implementation that will differ.

+1  A: 

There is not such concept as abstract class in Objective-C. In order to avoid the warning, you have to provide a default implementation in your base class. Usually, this implementation will throw a doesNotRespondToSelector error at runtime:

- (id)someMethod:(SomeObject*)blah 
     [self doesNotRecognizeSelector:_cmd];
     return nil;
}

Note: the _cmd argument is the invoked selector.

Laurent Etiemble
This is a workable alternative, but you really want an "abstract" class to be non-instantiable, not just make a given method raise an exception.
Quinn Taylor
+3  A: 

The superclass should not know about its subclasses. You should implement the - (void)doACommonBehaviour method in every subclass and there:

- (void)doACommonBehaviour
{     
      [super doACommonBehaviour];
      [self doAUniqueBehaviour];
}

EDIT - clarification:

If all the subclasses are going to implement -doAUniqueBehaviour then it should be implemented in the superclass (even empty) and each subclass will override it to its needs.

If subclass1 implements -doAUniqueBehaviour1, subclass2 implements -doAUniqueBehaviour2 etc then do what I propose above; eg. in subclass1:

- (void)doACommonBehaviour
{     
      [super doACommonBehaviour];
      [self doAUniqueBehaviour1];
}
Dimitris
What if I know for a fact that all subclasses implement `doAUniqueBehaviour`? I really want to provide the interface to this method in the superclass, just not the implementation.
Sam
If they all implement it, add it to the superclass - the interface and the implementation. And then override it in every subclass independently.
Dimitris
I assumed that -doAUniqueBehaviour is implemented by some subclasses. If it's implemented by all the subclasses (even in a different way) it should be implemented by the superclass. And then each subclass will do whatever it needs to do in its own implementation of this method. In any case, there is no need for a protocol, or for the superclass to check what the subclasses can do.
Dimitris
This works, so I won't vote it down, but it would be cleaner to have this method only in the parent class, and provide an empty `-doAUniqueBehaviour` method in the `.mm` file to prevent duplication.
Quinn Taylor
+1  A: 

@Dimitri's suggestion will work, but instead of forcing each subclass to implement the same method, you can declare it once in Block, and just above that method (in the implementation file, not header) declare the unique method like so:

- (void) doUniqueBehaviour { }

- (void) doCommonBehaviour {     
 // any common code you need
 [self doUniqueBehaviour];
}

This will prevent any compiler warnings, and you can override -doUniqueBehaviour in subclasses as you like. It also avoids code duplication and reduces the potential for changing the code in one subclass but not another. Plus, you don't need a separate protocol, and dynamic typing is preserved.

Quinn Taylor
Yes, my answer initially assumed that -doAUniqueBehaviour is different in each subclass. More like doAUniqueBehaviour1 doAUniqueBehaviour2 etc... If all the classes implement it, it should be in the superclass.
Dimitris