Objets composites Objective-C

Nous pouvons créer une sous-classe dans un cluster de classes qui définit une classe qui y intègre un objet. Ces objets de classe sont des objets composites. Vous vous demandez peut-être ce qu'est un cluster de classes. Nous allons donc voir d'abord ce qu'est un cluster de classes.

Clusters de classes

Les clusters de classes sont un modèle de conception dont le framework de base fait un usage intensif. Les clusters de classes regroupent un certain nombre de sous-classes concrètes privées sous une superclasse abstraite publique. Le regroupement de classes de cette manière simplifie l'architecture visible publiquement d'un framework orienté objet sans réduire sa richesse fonctionnelle. Les clusters de classes sont basés surabstract factory design pattern.

Pour simplifier les choses, au lieu de créer plusieurs classes pour des fonctions similaires, nous créons une seule classe qui se chargera de sa gestion en fonction de la valeur d'entrée.

Par exemple, dans NSNumber, nous avons de nombreux clusters de classes comme char, int, bool et ainsi de suite. Nous les regroupons tous dans une seule classe qui s'occupe de gérer les opérations similaires dans une seule classe. NSNumber encapsule en fait la valeur de ces types primitifs dans des objets.

Qu'est-ce qu'un objet composite?

En incorporant un objet de cluster privé dans un objet de notre propre conception, nous créons un objet composite. Cet objet composite peut s'appuyer sur l'objet cluster pour ses fonctionnalités de base, en interceptant uniquement les messages que l'objet composite souhaite gérer d'une manière particulière. Cette architecture réduit la quantité de code que nous devons écrire et vous permet de tirer parti du code testé fourni par Foundation Framework.

Ceci est expliqué dans la figure suivante.

L'objet composite doit se déclarer comme une sous-classe de la superclasse abstraite du cluster. En tant que sous-classe, elle doit remplacer les méthodes primitives de la superclasse. Il peut également remplacer les méthodes dérivées, mais ce n'est pas nécessaire car les méthodes dérivées fonctionnent à travers les méthodes primitives.

La méthode count de la classe NSArray est un exemple; l'implémentation par l'objet intervenant d'une méthode qu'il remplace peut être aussi simple que -

- (unsigned)count  {
   return [embeddedObject count];
}

Dans l'exemple ci-dessus, l'objet incorporé est en fait de type NSArray.

Un exemple d'objet composite

Maintenant, pour voir un exemple complet, regardons l'exemple de la documentation Apple qui est donné ci-dessous.

#import <Foundation/Foundation.h>

@interface ValidatingArray : NSMutableArray {
   NSMutableArray *embeddedArray;
}

+ validatingArray;
- init;
- (unsigned)count;
- objectAtIndex:(unsigned)index;
- (void)addObject:object;
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
- (void)removeLastObject;
- (void)insertObject:object atIndex:(unsigned)index;
- (void)removeObjectAtIndex:(unsigned)index;

@end

@implementation ValidatingArray
- init {
   self = [super init];
   if (self) {
      embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init];
   }
   return self;
}

+ validatingArray {
   return [[self alloc] init] ;
}

- (unsigned)count {
   return [embeddedArray count];
}

- objectAtIndex:(unsigned)index {
   return [embeddedArray objectAtIndex:index];
}

- (void)addObject:(id)object {
   if (object != nil) {
      [embeddedArray addObject:object];
   }
}

- (void)replaceObjectAtIndex:(unsigned)index withObject:(id)object; {
   if (index <[embeddedArray count] && object != nil) {
      [embeddedArray replaceObjectAtIndex:index withObject:object];
   }
}

- (void)removeLastObject; {
   if ([embeddedArray count] > 0) {
      [embeddedArray removeLastObject];
   }
}

- (void)insertObject:(id)object atIndex:(unsigned)index; {
   if (object != nil) {
      [embeddedArray insertObject:object atIndex:index];
   }
}

- (void)removeObjectAtIndex:(unsigned)index; {
   if (index <[embeddedArray count]) {
      [embeddedArray removeObjectAtIndex:index];
   }
}

@end

int main() {
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   ValidatingArray *validatingArray = [ValidatingArray validatingArray];
   
   [validatingArray addObject:@"Object1"];
   [validatingArray addObject:@"Object2"];
   [validatingArray addObject:[NSNull null]];
   [validatingArray removeObjectAtIndex:2];
   NSString *aString = [validatingArray objectAtIndex:1];
   NSLog(@"The value at Index 1 is %@",aString);
   [pool drain];
   
   return 0;
}

Maintenant, lorsque nous compilons et exécutons le programme, nous obtiendrons le résultat suivant.

2013-09-28 22:03:54.294 demo[6247] The value at Index 1 is Object2

Dans l'exemple ci-dessus, nous pouvons voir que la validation d'une fonction du tableau ne permettrait pas d'ajouter des objets nuls qui entraîneraient un crash dans le scénario normal. Mais notre tableau de validation s'en charge. De même, chacune des méthodes de validation du tableau ajoute des processus de validation en dehors de la séquence normale d'opérations.