Pendant que j'écris du code POO, j'applique certaines pratiques d'objets élégants.
L'un d'eux est que les cours doivent être définitifs. Cela signifie qu'ils ne peuvent pas être étendus par héritage mais uniquement par composition.
L'avantage est la simplicité. Je veux dire que, de cette manière, chaque objet est vu comme un bloc cohérent. Ce qui intéresse ses clients, c'est son comportement exposé. Rien de plus. Au lieu de cela, grâce à l'extension, un client peut le casser.
Par exemple, un objet peut mettre en relation deux de ses méthodes. Donc, si nous pouvons remplacer par extension l'un d'eux, nous pouvons casser l'autre. Pour cette raison, pour être sûr, nous devrions vérifier sa mise en œuvre. On augmente ainsi le couplage entre l'étendu et l'extension.
En d'autres termes, les classes finales renforcent l'idée que nous ne devrions nous soucier que du comportement exposé. Et pas de mise en œuvre. Néanmoins, cela nécessite un changement de notre façon de raisonner à leur sujet. Le modèle Alias simplifie un aspect de ce changement.
Le pattern Alias permet d'étendre la façon dont une classe peut construire ses objets sans les sous-classer ou les modifier.
Supposons une classe finale qui crée ses objets en utilisant certains paramètres obligatoires. Comment ajouter une autre manière de créer ses objets ? Par exemple, comment pouvons-nous ajouter un constructeur qui utilise une valeur par défaut pour un ou plusieurs de ses paramètres manquants ?
Une approche pourrait être d'ajouter un autre constructeur à la classe. Mais cela pourrait devenir incontrôlable. De plus, cela pourrait ne pas être possible. Par exemple, la classe finale susmentionnée pourrait se trouver dans une bibliothèque externe.
Un autre inconvénient de cette approche est que nous pouvons polluer la classe finale. Par exemple, nous pouvons avoir une classe finale qui construit ses objets avec un JSON. Mais après un certain temps, nous devons également ajouter XML. Comme vous pouvez l'imaginer, ajouter le code pour mapper XML à JSON polluera inévitablement cette classe.
Cependant, le modèle Alias n'est pas limité aux classes finales. Par exemple, nous ne pouvons pas avoir deux constructeurs avec les mêmes paramètres mais une sémantique différente.
Pour résoudre ce problème, nous pouvons ajouter des méthodes de fabrique statiques au code de la classe. Mais les mêmes inconvénients susmentionnés affectent cette approche. C'est-à-dire : cela devient incontrôlable ; cela ne pouvait pas toujours être possible ; cela va polluer la classe.
Une meilleure approche des deux problèmes consiste à créer une autre classe avec le comportement de constructeur souhaité. Cette classe encapsule sa propre logique de construction. Et il déléguera tout à l'autre classe, y compris la création proprement dite. C'est le modèle Alias.
Utilisez le modèle Alias lorsque :
Vous devez ajouter ou modifier un constructeur d'une classe finale ;
Vous souhaitez ajouter ou modifier un constructeur d'une classe sans le modifier ou le sous-classer ;
Vous avez besoin de deux constructeurs ou plus d'une classe avec les mêmes paramètres sans le modifier ou le sous-classer.
La structure est simple. Nous avons besoin d'au moins deux classes qui implémentent la même interface : un Alias
et un Aliased.
AnInterface
déclare une interface.
Aliased
implémente AnInterface
;
expose un ou plusieurs constructeurs.
Alias
AnInterface
;Aliased
;Aliased
référencé.Alias
construit - selon ses propres règles - un objet Aliased
et en conserve une référence. Ensuite, il délègue tout à l'alias.
Le modèle d'alias a les conséquences suivantes :
Pour implémenter le modèle Alias, vous avez besoin :
définir une interface ;
pour implémenter l'interface précédemment définie avec une classe. Ce sera l'alias ;
pour implémenter l'interface précédemment définie avec une classe alias, et il vous faut :
Le code Java ci-dessous exprime le modèle Alias. Dans ce code, l'alias injecte une valeur par défaut pour un paramètre autrement obligatoire :
interface AnInterface { void aMethod(); Something anotherMethod(); } final class Aliased implements AnInterface { private final A a; private final B b; Aliased(final A a, final B b) { this.a = a; this.b = b; } void aMethod() { // implementation } Something anotherMethod() { // implementation } } final class Alias implements AnInterface { private final Aliased aliased; Alias(final A a) { this( new Aliased( a, new InstanceOfB(...) ) ); } private Alias(final Aliased aliased) { this.aliased = aliased; } void aMethod() { this.aliased.aMethod(); } Something anotherMethod() { return this.aliased.anotherMethod(); } }
Dans une certaine mesure, le motif Alias pourrait également être considéré comme un moyen de décorer la construction des objets. Cette vision est particulièrement vraie si nous voyons une classe comme un objet dont la responsabilité est de créer des objets.