Il ne s'agit pas de savoir lequel est le meilleur, mais de savoir quand utiliser quoi.
Dans les cas "normaux", une simple question suffit pour savoir si nous avons besoin d'un héritage ou d'une agrégation.
- Si la nouvelle classe est plus ou moins comme la classe d'origine. Utilisez l'héritage. La nouvelle classe est maintenant une sous-classe de la classe d'origine.
- Si la nouvelle classe doit ont la classe originale. Utilisez l'agrégation. La nouvelle classe a maintenant la classe originale comme membre.
Cependant, il y a une grande zone grise. Nous avons donc besoin de plusieurs autres astuces.
- Si nous avons utilisé l'héritage (ou si nous prévoyons de l'utiliser) mais que nous n'utilisons qu'une partie de l'interface, ou si nous sommes obligés de remplacer un grand nombre de fonctionnalités pour que la corrélation reste logique. Nous avons alors une grosse odeur désagréable qui indique que nous devions utiliser l'agrégation.
- Si nous avons utilisé l'agrégation (ou si nous prévoyons de l'utiliser) mais que nous découvrons que nous devons copier presque toutes les fonctionnalités. Nous avons alors une odeur qui pointe dans la direction de l'héritage.
Pour faire court. Nous devons utiliser l'agrégation si une partie de l'interface n'est pas utilisée ou doit être modifiée pour éviter une situation illogique. Nous ne devons utiliser l'héritage que si nous avons besoin de presque toutes les fonctionnalités sans changements majeurs. Et en cas de doute, utilisez l'agrégation.
Une autre possibilité, dans le cas où nous avons une classe qui a besoin d'une partie de la fonctionnalité de la classe originale, est de diviser la classe originale en une classe racine et une sous-classe. Et laisser la nouvelle classe hériter de la classe racine. Mais il faut faire attention à ne pas créer une séparation illogique.
Ajoutons un exemple. Nous avons une classe "Dog" avec les méthodes suivantes : "Eat", "Walk", "Bark", "Play".
class Dog
Eat;
Walk;
Bark;
Play;
end;
Nous avons maintenant besoin d'une classe 'Cat', qui a besoin de 'Eat', 'Walk', 'Purr', et 'Play'. Essayez d'abord de l'étendre à partir d'un chien.
class Cat is Dog
Purr;
end;
Ça a l'air, d'accord, mais attendez. Ce chat peut aboyer (les amoureux des chats vont me tuer pour ça). Et un chat qui aboie viole les principes de l'univers. Donc nous devons remplacer la méthode Bark pour qu'elle ne fasse rien.
class Cat is Dog
Purr;
Bark = null;
end;
Ok, ça marche, mais ça sent mauvais. Alors essayons une agrégation :
class Cat
has Dog;
Eat = Dog.Eat;
Walk = Dog.Walk;
Play = Dog.Play;
Purr;
end;
Ok, c'est sympa. Ce chat n'aboie plus, il n'est même plus silencieux. Mais il a toujours un chien interne qui veut sortir. Alors essayons la solution numéro trois :
class Pet
Eat;
Walk;
Play;
end;
class Dog is Pet
Bark;
end;
class Cat is Pet
Purr;
end;
C'est beaucoup plus propre. Pas de chiens internes. Et les chats et les chiens sont au même niveau. On peut même introduire d'autres animaux domestiques pour étendre le modèle. Sauf si c'est un poisson, ou quelque chose qui ne marche pas. Dans ce cas, nous devons à nouveau procéder à un remaniement. Mais ce sera pour une autre fois.