2012-03-14 15 views
18

Diciamo che ho proprietà A su classA e proprietà B su classB e voglio classAB avere entrambe le proprietà A e B. Non riesco ancora a capire come fare in modo che tutto funzioni con la composizione .Come funziona la composizione di classe in iOS?

Mi rendo conto che questo può essere fatto con l'ereditarietà, ma voglio imparare come farlo con la composizione . Ho esaminato alcuni esempi e ancora non capisco come funziona.

risposta

20

Si crea una nuova classe, che ha istanze di classA e classB come variabili membro. Quindi si implementano le proprietà passando attraverso i metodi get/set.

@interface ClassAB 
{ 
    ClassA *objectA; 
    ClassB *objectB; 
} 
@property (nonatomic,strong) id propertyA; 
@property (nonatomic,strong) id propertyB; 
@end 

@implementation ClassAB 
- (id)propertyA { return objectA.propertyA; } 
- (void)setPropertyA:(id)value { objectA.propertyA = value; } 
- (id)propertyB { return objectB.propertyB; } 
- (void)setPropertyB:(id)value { objectB.propertyB = value; } 
@end 

E questo è la composizione. Alcune lingue hanno una sintassi speciale per farlo (ad es., In Ruby puoi includere un insieme di metodi da una classe/modulo in un'altra), ma Objective-C non lo consente.

Una cosa che può fare fare in Objective-C è catturare i messaggi inviati all'oggetto che non hanno un metodo associato e inoltrarli a un altro oggetto. Questo trucco è utile se stai scrivendo una classe che si porrà come un'altra classe, o se ci sono molti messaggi diversi da inoltrare e non vuoi scriverli tutti manualmente.

Lo svantaggio di utilizzare l'inoltro dei messaggi è che si rinuncia a un certo controllo e può essere più difficile prevedere quando un messaggio verrà gestito dalla classe o dalla classe di inoltro. Ad esempio, se una superclasse implementa un metodo, tale metodo verrà eseguito e il codice di inoltro non verrà chiamato.

+0

@Jackson prima di accettare questa risposta, lascia che scriva qualcosa con KVO che ti consenta di farlo in un modo più elegante. –

+2

Credo che questa sia la soluzione migliore per la leggibilità. La soluzione di Richards è un trucco eccellente, ma metto sempre pragmatismo e leggibilità su un codice complicato. Questione di stile immagino – Slappy

+0

@Slappy Ho usato quello che è essenzialmente l'approccio di Richard in una sola occasione - quando avevo codice esistente che non potevo modificare a cui dovevo passare un 'NSString' (che è un cluster di classe e quindi sottoclassi non è banale) a cui volevo una banda laterale di comunicazioni. Gli oggetti associati al runtime erano il principale approccio alternativo preso in considerazione, ma sentivo che il 'forwardingTargetForSelector:' (anche se per creare una sottoclasse di finzione piuttosto che comporre nel mio caso) era la soluzione più pulita e più leggibile. – Tommy

8

Supponendo che ClassA e ClassB siano implementati come hai detto, funziona molto bene ed è facilmente estensibile.

@interface ClassAB : NSObject 

@property int a; 
@property int b; 

@property ClassA *aObject; 
@property ClassB *bObject; 

@end 

@implementation ClassAB 

@dynamic a, b; 
@synthesize aObject, bObject; 

-(id) forwardingTargetForSelector:(SEL)aSelector 
{ 
    if ([aObject respondsToSelector:aSelector]) 
     return aObject; 
    else if ([bObject respondsToSelector:aSelector]) 
     return bObject; 

    return nil;  
} 

@end 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     ClassA *a = [ClassA new]; 
     ClassB *b = [ClassB new]; 

     ClassAB *ab = [ClassAB new]; 
     ab.aObject = a; 
     ab.bObject = b; 

     ab.a = 10; 
     ab.b = 20; 

     NSLog(@"%i, %i", a.a, b.b); // outputs 10, 20 
    } 
    return 0; 
} 
+0

Oh, Richard J. Ross III, come mai posti così spesso i miei pensieri prima di farlo? Di solito mi accorgo in tempo per evitare di premere il pulsante ... potrebbe valere la pena di sottolineare esplicitamente che se si lancia 'ab' per digitare' ClassA', si comporterebbe esattamente come una vera istanza di 'ClassA' per tutti i metodi definiti e proprietà e se la lanci in 'ClassB' si comporterebbe allo stesso modo per tutti i metodi e le proprietà definiti che non sono definiti anche da' ClassA'? – Tommy

+0

@Tommy deve essere il mio ninja sensi :) –

+0

@Tommy che in realtà non è corretto se 'ClassAB' implementa un selettore che' ClassA' fa pure, nel qual caso verrà chiamata l'implementazione 'ClassAB' (Ad esempio, un'abitudine implementazione '-description'). –

Problemi correlati