2010-03-21 12 views
26

En Ruby, hay módulos y puede ampliar una clase "mezclando" el módulo.¿Objective-C admite Mixin como Ruby?

module MyModule 
    def printone 
    print "one" 
    end 
end 

class MyClass 
    include MyModule 
end 

theOne = MyClass.new 
theOne.printone 
>> one 

En Objective-C, me parece que tengo un conjunto de métodos comunes que quiero una serie de Clase "heredar". ¿De qué otras maneras puedo lograr esto sin crear una clase común y derivar todo de esa clase común?

Respuesta

28

Editar: se han agregado cambios porque algunas personas creen que soy responsable de las limitaciones de Objective-C.

Respuesta corta: no se puede. Objective-C no tiene el equivalente de Ruby mixins.

Ligeramente menos respuesta corta: Objective-C tiene algo con el mismo sabor: protocolos. Los protocolos (Interfaces en algunos otros idiomas) son una forma de definir un conjunto de métodos que una clase que adopta esos protocolos se compromete a implementar. Sin embargo, un protocolo no proporciona una implementación. Esa limitación evita el uso de protocolos como un equivalente exacto a Ruby mixins.

Aún menos respuesta corta: Sin embargo, el tiempo de ejecución de Objective-C tiene una API expuesta que le permite jugar con las características dinámicas del idioma. Luego sales del idioma, pero puedes tener protocolos con implementaciones predeterminadas (también llamadas protocolos concretos). La respuesta de Vladimir muestra una forma de hacer eso. En ese punto, me parece que obtienes Ruby mixins bien.

Sin embargo, no estoy seguro de que lo recomiende. En la mayoría de los casos, otros patrones se ajustan a la factura sin jugar con el tiempo de ejecución. Por ejemplo, puede tener un subobjeto que implemente el método mixto (tiene-a en lugar de is-a). Jugando con el tiempo de ejecución está bien, pero tiene 2 inconvenientes:

  • Usted hace su código menos legible, ya que requiere que los lectores saben mucho más de la lengua. Claro que puedes (y deberías) comentarlo, pero recuerda que cualquier comentario necesario puede verse como un defecto de implementación.

  • Depende de que implementación del idioma. Claro, las plataformas de Apple son, con mucho, las más comunes para Objective-C, pero no se olvide de Cocotron o GnuStep (o Etoilé) que tienen diferentes tiempos de ejecución, que pueden o no ser compatibles con Apple en ese sentido.

Como nota al margen, declaro a continuación que las categorías no pueden agregar el estado (variables de instancia) a una clase. Al usar la API de tiempo de ejecución, puede levantar esa limitación también. Sin embargo, esto está más allá del alcance de esta respuesta.

Respuesta larga:

Dos características de Objective-C se ven como posibles candidatos: categorías y protocolos. Las categorías no son realmente la opción correcta aquí, si entiendo la pregunta correctamente. La característica correcta es un protocolo.

Déjeme dar un ejemplo. Supongamos que quiere que un grupo de sus clases tenga una habilidad específica llamada "cantar".A continuación, se define un protocolo:

@protocol Singer 
    - (void) sing; 
@end 

Ahora puede declarar que ninguna de sus propias clases adopta el protocolo de la siguiente manera:

@interface Rectangle : Shape <Singer> { 
    <snip> 
@end 

@interface Car : Vehicle <Singer> { 
    <snip> 
@end 

Al declarar que adopten el protocolo se comprometen a aplicar el sing método. Por ejemplo:

@implementation Rectangle 

- (void) sing { 
    [self flashInBrightColors]; 
} 

@end 

@implementation Car 

- (void) sing { 
    [self honk]; 
} 

@end 

continuación, utiliza estas clases, por ejemplo, así:

void choral(NSArray *choir) // the choir holds any kind of singer 
{ 
    id<Singer> aSinger; 
    for (aSinger in choir) { 
     [aSinger sing]; 
    } 
} 

en cuenta que los cantantes de la matriz no necesita tener una superclase común. Observe también que una clase puede tener solo una superclase, pero muchos protocolos adoptados. Note finalmente que la verificación de tipos es realizada por el compilador.

En efecto, el mecanismo de protocolo es la herencia múltiple utilizada para el patrón de mixin. Esa herencia múltiple está severamente limitada porque un protocolo no puede agregar nuevas variables de instancia a una clase. Un protocolo solo describe una interfaz pública que los adoptantes deben implementar. A diferencia de los módulos de Ruby, no contiene una implementación.

Eso es lo máximo. Sin embargo, mencionaremos las categorías.

Una categoría se declara no entre corchetes angulares, sino entre paréntesis. La diferencia es que se puede definir una categoría para una clase existente para expandirla sin subclasificarla. Incluso puede hacerlo para una clase de sistema. Como se puede imaginar, es posible usar categorías para implementar algo similar a mixin. Y se utilizaron de esa manera durante un tiempo prolongado, generalmente como categoría NSObject (la raíz típica de la jerarquía de herencia), a tal punto que se llamaron protocolos "informales".

Es informal porque 1- el compilador no realiza ninguna comprobación de tipo, y 2- la implementación de los métodos de protocolo es opcional.

No es necesario utilizar categorías como protocolos, especialmente porque los protocolos formales ahora pueden declarar que algunos de sus métodos son opcionales con la palabra clave @optional o son necesarios (por defecto) con @required.

Las categorías siguen siendo útiles para agregar algún comportamiento específico de dominio a una clase existente. NSString es un objetivo común para eso.

También es interesante señalar que la mayoría (si no todas) de NSObject instalaciones están de hecho declaradas en un protocolo NSObject. Esto significa que no es realmente convincente utilizar NSObject como una superclase común para todas las clases, aunque esto todavía se hace comúnmente por razones históricas, y bueno ... porque no hay ningún inconveniente para hacerlo. Pero algunas clases del sistema, como NSProxy, son , noNSObject.

+0

@jdmuys: Esta respuesta es muy completa, pero olvidé un pequeño detalle. Espero que esté bien para ti, tomé la libertad de agregarlo. –

+0

maldita sea! Estaba editando al mismo tiempo. Me temo que tu edición podría haberse perdido. ¡Puedes rehacerlo! :-) –

+0

@jdmuys: No hay problema. Así es como (el darwinista) la concurrencia optimista funciona aquí en SO :-) –

31

enchufe Shameless: ObjectiveMixin

Se aprovecha de la capacidad de Objective-C de tiempo de ejecución de la adición de métodos para una clase en tiempo de ejecución (en oposición a categorías, que son solamente tiempo de compilación). Compruébalo, funciona bastante bien y de manera similar a los mixins de Ruby.

+4

En una multitud de respuestas de "No se puede.", Usted proporciona no solo una forma que puede, sino una lib. Excelente trabajo. –

+0

¡Esto se ve genial, gracias por compartir! –

+0

¡Vaca acebo! Me encanta eso Siempre me siento frustrado por la falta de herencia múltiple de mi idioma favorito. – MonsieurDart

11

Literalmente puede mezclar el código usando #include. Esto no es aconsejable y está en contra de todas las religiones en Objectivo-C, sin embargo funciona perfectamente.

Por favor, no lo hagas en el código de producción.

por ejemplo en el archivo:

MixinModule.header (no debe ser compilado o copiado a la meta)

-(void)hello; 

MixinModule.body (no debe ser compilado o copiado hasta el objetivo)

-(void)hello{ 
    NSLog(@"Hello"); 
} 

en clase mixin:

@interface MixinTest : NSObject 
#include "MixinModule.header" 
@end 

@implementation MixinTest 
#include "MixinModule.body" 
@end 

caso de uso:

#import <Foundation/Foundation.h> 

int main(int argc, const char * argv[]){ 
    @autoreleasepool { 
     [[[MixinTest new] autorelease] hello]; 
    } 
    return 0; 
} 

Por favor, no lo hacen en el código de producción.

+0

Esto es increíble! – Berik

+0

Todo el mundo que vota por votos esto está pensando "Sí, eso es super hacky ... pero tal vez solo pueda usarlo esta vez ..." –