Es común que en una jerarquía de clases haya algún comportamiento que este presente en todas ellas pero se materialice en forma diferente en cada una.

Una clase abstracta es un tipo especial de clase que no puede ser instanciado, existe para representar un concepto compartido por una serie de entidades. Las características incompletas de una clase abstracta son heredada por un grupo de subclases, que agregan, en diferentes maneras, las piezas faltantes en el padre.

Tipicamente en objective-c las clases son abstractas por convención, si el autor documenta una clase como abstracta, esta no debe instanciarse. Objective-c no posee una instrucción que le indique al compilador que evite su instanciación.

Sin embargo hay algunas técnicas para evitar la instanciación de una clase, o forzar la implementación de ciertos metodos en las subclases.

Un ejemplo de una jerarquía de clases, en la que se podría definir una clase abstracta, es la representación de un conjunto de medios de transportes. Por ejemplo un vehículo es un concepto abstracto que define a una entidad que posee la capacidad de desplazarse desde el punto A al B, por ser una abstracción no puede existir en si mismo, pero si pueden existir diferentes entidades que formen parte del conjunto de los vehículos, por ejemplo un auto, avión o una bicicleta. Estas tres entidades, por compartir el concepto de vehículo, poseen la capacidad de desplazarse, y cada una logra el desplazamiento de una forma en particular.

Definimos la interface de la clase Vehiculo, Esta clase define un método llamado “viajar”, este método le permite a los vehículos desplazarse de un punto a otro, sin embargo, como hacer esta tarea depende de cada tipo particular de vehículo

  1. @interface Vehiculo : NSObject {
  2.  
  3. }
  4. -(void)viajar;
  5.  
  6. @end

En la implementación del constructor de la clase Vehiculo, verificamos cual es la clase del objeto que vamos a instanciar y generamos una excepción en caso de que coincida con la clase Vehiculo.

  1. @implementation Vehiculo
  2.  
  3. - (id)init{
  4.     if ([[self class] isEqual:[Vehiculo class]]) {
  5.         [NSException raise:@"Abstract Class Exception" format:@"Error, la clase Vehiculo (Abstracta) no puede ser instanciada."];
  6.         [self release];
  7.         return nil;
  8.     }
  9.     if (self = [super init]) {
  10.         return self;
  11.     }
  12.     return nil;
  13. }

Al momento de implementar el método viajar, podríamos forzar a las subclases a que implementen este método si es que lo va a ejecutar, o simplemente dejar el cuerpo del mismo vacío.

  1. -(void)viajar{
  2. [NSException raise:@"Abstract Class Exception" format:@"El Metodo \"viajar\" debe ser implementado en las subclases"];
  3. }
  4.  
  5. @end

Como ejemplo definimos la clase Auto, la cual hereda a Vehiculo, e implementa el método “viajar”

  1. #import "Vehiculo.h"
  2.  
  3. @interface Auto : Vehiculo {
  4.  
  5. }
  6.  
  7. @end
  8.  
  9. @implementation Auto
  10.  
  11. -(void)viajar{
  12.     NSLog(@"Viajando por la ciudad en auto");
  13. }
  14.  
  15. @end

y también definimos la clase Avion, en este caso no redefinimos el método “viajar”

  1. @interface Avion : Vehiculo {
  2.  
  3. }
  4.  
  5. @end
  6.  
  7. #import "Avion.h"
  8.  
  9. @implementation Avion
  10.  
  11. @end

Al momento de instanciar las estas clases y ejecutar el método “viajar” obtenemos los siguientes resultados.

  1. @try {
  2.     Vehiculo * _vehiculo = [[Vehiculo alloc]init];
  3.     [_vehiculo viajar];
  4. }
  5. @catch (NSException * e) {
  6.     NSLog(@"%@",[e description]);//Error, la clase Vehiculo (Abstracta) no puede ser instanciada.
  7. }

La ejecución del constructor genera una excepción, la cual es capturada, y se loggea en la consola el error:

“Error, la clase Vehiculo (Abstracta) no puede ser instanciada.”

  1. @try {
  2.     Vehiculo * _avion = [[Avion alloc]init];
  3.     [_avion viajar];
  4. }
  5. @catch (NSException * e) {
  6.     NSLog(@"%@",[e description]);//El Metodo "viajar" debe ser implementado en las subclases
  7.  
  8. }

Podemos instanciar la clase Avion, pero al momento de ejecutar el método viajar, se genera una excepción, la cual es capturada, y se loggea en la consola el error:

“El Metodo ‘viajar’ debe ser implementado en las subclases”

  1. @try {
  2.     Vehiculo * _auto = [[Auto alloc]init];
  3.     [_auto viajar];// Viajando por la ciudad en auto
  4. }
  5. @catch (NSException * e) {
  6.     NSLog(@"%@",[e description]);
  7. }

En este ultimo caso podemos instanciar la clase Auto, y la ejecución del método viajar loggea en consola el mensaje:

” Viajando por la ciudad en auto”

Si bien tener estas verificaciones nos permite evitar que se creen instancias de determinada clases, debemos ser cuidadosos, ya que vamos a recibir el mensaje de error durante la ejecución del programa, no en el momento de la compilación. Por eso es importante dejar documentado en forma clara que una clase no debe ser instanciada, y respetar siempre el protocolo de la misma.

Descarga el proyecto con el codigo del ejemplo JCGAbstractClass