Le motif de décorateur est généralement utilisé pour étendre la fonctionnalité d'un objet en prolongeant l'une de ses méthodes de courant.
Pour illustrer, veuillez envisager un objet object
et un décorateur decorator
. object
a une méthode appelée methodA()
. decorator
, car nous sommes dans le motif de décorateur, cette méthode présente également cette méthode par héritage (à la fois object
et decorator
_ éminence hériter de la même super-classe qui a methodA()
. Appelons-le AbstractObject
).
methodA()
In object
ressemble à ceci:
public void methodA(){
// do stuff
}
methodA()
In decorator
ressemble à ceci:
public void methodA(){
// do some extra stuff.
wrappedObject.methodA(); // and then delegate to the wrapped object.
}
Il serait utilisé comme ceci:
AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object.methodA(); // does stuff and some more stuff. the extended methodA().
Le point de decorator
est d'étendre la fonctionnalité de object
, et cela le fait en prolongeant l'une des méthodes de courant object
. Jusqu'à présent, décorateur classique.
Maintenant, ma question :
Comme je l'ai dit, le décorateur étend habituellement la fonctionnalité d'un objet en ajoutant du contenu à l'une des méthodes actuelles de l'objet.
Mais que se passe-t-il si je voulais utiliser décorateur pour ajouter des méthodes publiques à un objet?
Veuillez considérer le scénario suivant où decorator
a une autre méthode methodB()
:
AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
((ConcreteDecorator)object).methodB();
// methodB() belongs to ConcreteDecorator() but not to AbstractObject().
Comme vous le voyez, si je voulais faire cela, je devrais lancer object
à ConcreteDecorator
.
Cela expose le type concret de object
et le fait qu'il est décoré, et aussi quelque peu défaire l'intention du motif de décorateur. En outre, le code invoquant methodB()
devrait savoir object
est décoré. Encore une fois, pas beaucoup dans l'esprit de décorateur.
À la lumière de cela, j'ai deux questions :
Le motif de décorateur est-il jamais utilisé de cette façon? Est-il déjà utilisé pour ajouter des méthodes publiques à un objet, par opposition à seulement "s'étendant" de ses méthodes actuelles?
Y a-t-il des situations où ce serait une bonne idée ou une utilisation sage de décorateur?
Le motif de décorateur est généralement utilisé pour éviter une explosion de sous-classes. Un exemple courant implique des fenêtres UI que vous pouvez avoir une combinaison de n attributs (barre de défilement, barre de titre, redimensionnable, mobile, etc.). Soutenir toutes les combinaisons de ces n attributs impliquerait de faire des sous-classes de 2 ^ n. Le motif de décorateur empêche l'explosion en emballant autour de la classe de la fenêtre de base ' existant . Le motif de décorateur ne nécessiterait idéalement que n classes dans ce cas.
Pour le cas que vous avez décrit, vous ajoutez Nouveau Méthodes publiques; Par conséquent, le motif de décorateur n'est probablement pas idéal. Je ne peux penser à aucune situation où il serait idéal d'ajouter de nouvelles méthodes via un décorateur.
Quelques alternatives à considérer:
Il est valable d'utiliser le sous-typing pour ajouter des méthodes, à condition que vous gardes le SOLID Principes à l'esprit. Par conséquent, vous pourriez envisager d'étendre l'objet concret à prendre en charge la méthode, puis de concevoir le décorateur.
AbstractObject object = new ExtendedConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
(ConcreteDecorator)object.methodB();
// methodB() belongs to ExtendedConcreteObject() but not to AbstractObject()
Une autre façon de voir le problème: en ajoutant une méthode publique, vous modifiez l'interface de l'objet. Cela peut être fait en utilisant le modèle adaptateur . Pour garder le décorateur concret simple, vous pouvez envisager de créer un décorateur puis construire un adaptateur pour ce décorateur.
AbstractObject object = new ConcreteObject();
object.methodA(); // does stuff
object = new ConcreteDecorator(object);
object = new ConcreteAdapter(object);
(ConcreteAdapter)object.methodB();
// methodB() belongs to ConcreteAdapter() but not to AbstractObject() or ConcreteDecorator().
La décoration consiste à ajouter des modifications comportementales subtiles à une classe sous-jacente.
Je pense que votre intention est de prolonger une classe. Pensez à "séparation des préoccupations". En outre, le principe "ouvert/fermé" à l'esprit (que je n'ai personnellement pas tout à fait d'accord))
Le point de décoration est que vous passez un objet autour qui prend en charge une interface donnée. Si vous faites des trucs supplémentaires dans des méthodes supplémentaires, votre application devra être "coulé" à cet objet pour obtenir le comportement supplémentaire.
Downcasting à certains ingénieurs semble bien, tandis que d'autres ingénieurs pensent que ce n'est pas une solution propre, car le code est jonché avec des mots-clés d'instance. Cela lie les classes à votre implémentation prolongée qui nécessite plus de diligence lors du refactoring.
Personnellement, la sous-emploi des objets doit être utilisée avec parcimonie et doit être cachée autant que possible des classes de consommation, car elle enfreint une interface.
Tout ce que je dis, c'est que c'est vraiment une question subjective. Fais attention.