Je me demandais ce qui rend l'itérateur spécial par rapport à d'autres constructions similaires, et qui a fait de la liste Gang of Four un modèle de conception.
L'itérateur est basé sur le polymorphisme (une hiérarchie de collections avec une interface commune) et la séparation des préoccupations (l'itération sur les collections doit être indépendante de la façon dont les données sont structurées).
Mais que se passe-t-il si nous remplaçons la hiérarchie des collections par, par exemple, une hiérarchie d'objets mathématiques (entier, flottant, complexe, matrice, etc.) et l'itérateur par une classe représentant certaines opérations connexes sur ces objets, par exemple les fonctions de puissance. Le diagramme de classe serait le même.
Nous pourrions probablement trouver de nombreux autres exemples similaires comme Writer, Painter, Encoder, et probablement de meilleurs exemples, qui fonctionnent de la même manière. Cependant, je n'ai jamais entendu aucun d'entre eux s'appeler un motif de conception.
Alors, qu'est-ce qui rend l'itérateur spécial?
Est-ce le fait qu'il est plus compliqué car il nécessite un état mutable pour stocker la position actuelle dans la collection? Mais alors, l'état mutable n'est généralement pas considéré comme souhaitable.
Pour clarifier mon propos, permettez-moi de donner un exemple plus détaillé.
Voici notre problème de conception:
Disons que nous avons une hiérarchie de classes et une opération définies sur les objets de ces classes. L'interface de cette opération est la même pour chaque classe, mais les implémentations peuvent être complètement différentes. Il est également supposé qu'il est judicieux d'appliquer l'opération plusieurs fois sur le même objet, par exemple avec des paramètres différents.
Voici une solution raisonnable à notre problème de conception (pratiquement une généralisation du modèle d'itérateur):
Pour la séparation des préoccupations, les implémentations de l'opération ne doivent pas être ajoutées en tant que fonctions à la hiérarchie de classe d'origine (objets opérande). Puisque nous voulons appliquer l'opération plusieurs fois sur le même opérande, elle doit être représentée par un objet contenant une référence à l'opérande, et pas seulement par une fonction. Par conséquent, l'opérande doit fournir une fonction qui renvoie l'objet représentant l'opération. Cet objet fournit une fonction qui exécute l'opération réelle.
Un exemple:
Il y a une classe de base ou une interface MathObject
(nom stupide, je sais, peut-être que quelqu'un a une meilleure idée.) Avec des classes dérivées MyInteger
et MyMatrix
. Pour chacun, MathObject
une opération Power
doit être définie qui permet le calcul du carré, du cube, etc. On pourrait donc écrire (en Java):
MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125