En quoi les modèles de proxy, de décorateur, d’adaptateur et de pont diffèrent-ils?

Je regardais le patron de proxy, et pour moi, cela ressemble énormément aux motifs Decorator, Adapter et Bridge. Est-ce que je comprends mal quelque chose? Quelle est la différence? Pourquoi devrais-je utiliser le modèle de proxy par rapport aux autres? Comment les avez-vous utilisés dans le passé dans des projets du monde réel?

Proxy, Decorator, Adapter et Bridge sont toutes des variantes de “wrapping” d’une classe. Mais leurs utilisations sont différentes.

  • Le proxy peut être utilisé lorsque vous souhaitez instancier un object paresseux ou masquer le fait que vous appelez un service distant ou contrôler l’access à l’object.

  • Le décorateur est aussi appelé “Smart Proxy”. Ceci est utilisé lorsque vous souhaitez append des fonctionnalités à un object, mais pas en étendant le type de cet object. Cela vous permet de le faire lors de l’exécution.

  • L’adaptateur est utilisé lorsque vous disposez d’une interface abstraite et que vous souhaitez mapper cette interface à un autre object ayant un rôle fonctionnel similaire, mais une interface différente.

  • Bridge est très similaire à Adapter, mais nous l’appelons Bridge lorsque vous définissez à la fois l’interface abstraite et l’implémentation sous-jacente. Si vous ne vous adaptez pas à un code hérité ou tiers, vous êtes le concepteur de tout le code, mais vous devez pouvoir échanger différentes implémentations.

  • Facade est une interface de niveau supérieur (read: simpler) avec un sous-système d’une ou plusieurs classes. Supposons que vous ayez un concept complexe qui nécessite la représentation de plusieurs objects. Apporter des modifications à cet ensemble d’objects est déroutant, car vous ne savez pas toujours quel object a la méthode dont vous avez besoin d’appeler. C’est le moment d’écrire une façade qui fournit des méthodes de haut niveau pour toutes les opérations complexes que vous pouvez effectuer dans la collection d’objects. Exemple: un modèle de domaine pour une section scolaire, avec des méthodes telles que countStudents() , reportAttendance() , assignSubstituteTeacher() , etc.

Comme le dit la réponse de Bill, leurs cas d’utilisation sont différents .

Ainsi sont leurs structures.

  • Proxy et Decorator ont tous deux la même interface que leurs types encapsulés, mais le proxy crée une instance sous le capot, tandis que le décorateur prend une instance dans le constructeur.

  • L’adaptateur et la façade ont tous deux une interface différente de celle qu’ils enveloppent. Mais l’adaptateur dérive d’une interface existante, tandis que la façade crée une nouvelle interface.

  • Bridge et Adapter pointent tous deux vers un type existant. Mais le pont indiquera un type abstrait et l’adaptateur pourrait pointer vers un type concret. Le pont vous permettra d’associer l’implémentation à l’exécution, alors que l’adaptateur ne le fera généralement pas.

Ma prise sur le sujet.

Les quatre modèles ont beaucoup en commun, tous les quatre sont parfois appelés de manière informelle des wrappers, ou des motifs d’encapsulation. Toutes les compositions d’utilisation, d’enrobage du sujet et de délégation de l’exécution au sujet à un moment donné, mappent un appel de méthode à un autre. Ils évitent au client de devoir créer un object différent et de copier toutes les données pertinentes. S’ils sont utilisés judicieusement, ils économisent de la mémoire et du processeur.

En promouvant le couplage lâche, ils rendent le code une fois stable moins exposé aux changements inévitables et mieux lisible par les autres développeurs.

Adaptateur

L’adaptateur adapte le sujet (adaptee) à une interface différente. De cette façon, nous pouvons append un object à une collection de types nominalement différents.

Les adaptateurs n’exposent que les méthodes pertinentes au client, peuvent restreindre toutes les autres, révéler les intentions d’utilisation de certains contextes, comme adapter une bibliothèque externe, la rendre moins générale et mieux cibler les besoins de nos applications. Les adaptateurs augmentent la lisibilité et l’auto-description de notre code.

Les adaptateurs protègent une équipe des codes volatils des autres équipes. un outil de sauveteur lorsque vous traitez avec des équipes offshore 😉

Moins mentionné vise à empêcher la classe de sujet d’excès d’annotations. Avec autant de frameworks basés sur des annotations, cela devient plus important que jamais.

L’adaptateur permet de contourner la limitation Java d’un seul inheritance. Il peut combiner plusieurs adaptateurs sous une seule enveloppe donnant une impression d’inheritance multiple.

Du sharepoint vue du code, l’adaptateur est «mince». Il ne devrait pas append beaucoup de code à la classe adaptee, en plus de simplement appeler la méthode adaptee et les conversions de données occasionnelles nécessaires pour effectuer de tels appels.

Il n’y a pas beaucoup de bons exemples d’adaptateurs dans les bibliothèques JDK ou de base. Les développeurs d’applications créent des adaptateurs pour adapter les bibliothèques aux interfaces spécifiques aux applications.

Décorateur

Non seulement le décorateur délègue, mappe une méthode à une autre, mais il modifie également le comportement de certaines méthodes du sujet, il peut décider de ne pas appeler la méthode du sujet, déléguer à un object différent, un object assistant.

Les décorateurs ajoutent généralement des fonctionnalités (transparentes) à un object enveloppé, comme la journalisation, le cryptage, le formatage ou la compression. Cette nouvelle fonctionnalité peut apporter beaucoup de nouveau code. Par conséquent, les décorateurs sont généralement beaucoup plus gros que les adaptateurs.

Le décorateur doit être une sous-classe de l’interface du sujet. Ils peuvent être utilisés de manière transparente au lieu de ses sujets. Voir BufferedOutputStream, il s’agit toujours de OutputStream et peut être utilisé tel quel. C’est une différence technique majeure par rapport aux adaptateurs.

Les exemples de manuels scolaires de toute la famille des décorateurs se trouvent facilement dans JDK – le Java IO. Toutes les classes comme BufferedOutputStream , FilterOutputStream et ObjectOutputStream sont des décorateurs de OutputStream . Ils peuvent être en couches d’oignon, où un décorateur est décoré à nouveau, ajoutant plus de fonctionnalités.

Procuration

Le proxy n’est pas un wrapper typique. L’object encapsulé, le sujet proxy, peut ne pas exister au moment de la création du proxy. Le proxy le crée souvent en interne. Il peut s’agir d’un object lourd créé à la demande ou d’un object distant dans une machine virtuelle Java ou un autre nœud de réseau, voire un object non Java, un composant en code natif. Il ne doit pas nécessairement envelopper ou déléguer à un autre object du tout.

Les exemples les plus courants sont les proxys distants, les initialiseurs d’objects lourds et les proxys d’access.

  • Remote Proxy – sujet sur le serveur distant, JVM différent ou même non-Java. Proxy traduit les appels de méthode en appels RMI / REST / SOAP ou tout ce qui est nécessaire, protégeant ainsi le client de l’exposition à la technologie sous-jacente.

  • Lazy Load Proxy – initialise entièrement l’object uniquement la première utilisation ou la première utilisation intensive.

  • Access Proxy – contrôle l’access à l’object.

Façade

La façade est étroitement associée au principe de conception de la moindre connaissance (loi de Déméter). Facade est très similaire à Adapter. Ils enveloppent tous les deux, ils mappent un object sur un autre, mais ils diffèrent dans l’intention. Facade aplatit la structure complexe d’un graphe d’objects complexe, simplifiant l’access à une structure complexe.

La façade enveloppe une structure complexe, lui fournissant une interface plate. Cela empêche un object client d’être exposé à des relations internes dans la structure du sujet, favorisant ainsi un couplage lâche.

Pont

Variante plus complexe du modèle d’adaptateur où non seulement l’implémentation varie mais aussi l’abstraction. Il ajoute une indirection supplémentaire à la délégation. La délégation supplémentaire est le pont. Il sépare l’adaptateur même de l’adaptation de l’interface. Cela augmente la complexité plus que tout autre modèle d’emballage, donc appliquez avec précaution.

Différences dans les constructeurs

Les différences de motifs sont également évidentes lorsqu’on regarde leurs constructeurs.

  • Le proxy n’encapsule pas un object existant. Il n’y a pas de sujet dans constructeur.

  • Decorator and Adapter encapsule déjà un object existant, et tel est généralement le cas
    fourni dans le constructeur.

  • Le constructeur de façade prend l’élément racine d’un graphique d’object entier, sinon il ressemble à l’adaptateur.

Exemple concret – JAXB Marshalling Adapter . Le but de cet adaptateur est de mapper une classe plate simple à une structure plus complexe requirejse de manière externe et d’empêcher une classe d’object “polluée” avec des annotations excessives.

Il y a beaucoup de chevauchement dans de nombreux modèles du GoF. Ils sont tous construits sur le pouvoir du polymorphism et leurs intentions sont parfois très différentes. (stratégie vs état)

Ma compréhension des motifs a été multipliée par 100 après avoir lu les schémas de conception de Head First .

Je le recommande fortement!

Toutes les bonnes réponses des experts ont déjà expliqué ce que représente chaque modèle.

Je vais décorer des points clés.

Décorateur:

  1. Ajouter un comportement à l’object lors de l’exécution . L’inheritance est la clé pour obtenir cette fonctionnalité, ce qui est à la fois un avantage et un inconvénient pour ce modèle.
  2. Il modifie le comportement de l’interface.

par exemple (avec chaînage): classes de paquets java.io liées aux interfaces InputStream et OutputStream

 FileOutputStream fos1 = new FileOutputStream("data1.txt"); ObjectOutputStream out1 = new ObjectOutputStream(fos1); 

Procuration:

  1. Utilisez-le pour l’initialisation différée, l’amélioration des performances en mettant en cache l’object et en contrôlant l’access au client / à l’appelant . Il peut fournir un comportement alternatif ou appeler un object réel. Pendant ce processus, il peut créer un nouvel object.
  2. Contrairement à Decorator , qui permet de chaîner des objects, Proxy ne permet pas le chaînage.

Par exemple: classes de paquets java.rmi .

Adaptateur:

  1. Il permet à deux interfaces non liées de fonctionner ensemble à travers les différents objects , jouant éventuellement le même rôle.
  2. Il modifie l’interface d’origine .

par exemple java.io.InputStreamReader ( InputStream renvoie un Reader )

Pont:

  1. Cela permet aux abstractions et aux implémentations de varier indépendamment .
  2. Il utilise la composition plutôt que l’inheritance .

par exemple les classes de collection dans java.util . List implémentée par ArrayList .

Notes clés:

  1. L’adaptateur fournit une interface différente à son sujet. Le proxy fournit la même interface. Le décorateur fournit une interface améliorée.
  2. L’adaptateur modifie l’interface d’un object, Decorator améliore les responsabilités d’un object.
  3. Le décorateur et le mandataire ont des objectives différents mais des structures similaires
  4. L’adaptateur rend les choses fonctionnelles après leur conception; Bridge les fait fonctionner avant qu’ils ne le soient.
  5. Bridge est conçu pour permettre à l’abstraction et à l’implémentation de varier de manière indépendante. L’adaptateur est installé ultérieurement pour que les classes non liées fonctionnent ensemble
  6. Decorator est conçu pour vous permettre d’append des responsabilités à des objects sans sous-classement.

Jetez un coup d’œil aux grandes questions / articles SE concernant des exemples de divers modèles de conception

Quand utiliser le motif de décorateur?

Quand utilisez-vous le Pattern Bridge? En quoi est-ce différent du modèle d’adaptateur?

Différences entre les modèles de proxy et de décorateur

Ils sont assez similaires, et les lignes entre eux sont assez grises. Je vous suggère de lire les entrées de modèle de proxy et de décorateur dans le wiki c2.

Les entrées et les discussions y sont très nombreuses et elles renvoient également à d’autres articles pertinents. Au fait, le wiki c2 est excellent pour se demander quelles sont les nuances entre différents modèles.

Pour résumer les entrées de c2, je dirais qu’un décorateur ajoute / modifie le comportement, mais un proxy a plus à voir avec le contrôle d’access (instanciation paresseuse, access à distance, sécurité, etc.). Mais comme je l’ai dit, les lignes entre eux sont grises et je vois des références à des mandataires qui pourraient facilement être considérés comme des décorateurs et vice versa.

Ceci est une citation de Head First Design Patterns

Définitions appartient à réserver. Les exemples me appartiennent.

Decorator – Ne modifie pas l’interface, mais ajoute de la responsabilité. Supposons que vous ayez une interface de voiture, lorsque vous implémentez cela pour différents modèles de la voiture (s, sv, sl), vous devrez peut-être append plus de responsabilités pour certains modèles. Comme a toit ouvrant, airbag etc.

Adapter – Convertit une interface en une autre. Vous avez une interface de voiture et vous aimeriez qu’elle agisse comme une jeep. Donc, vous prenez la voiture, modifiez-la et transformez-la en jeep. Puisque ce n’est pas une vraie jeep. Mais agit comme une jeep.

Facade – Rend une interface plus simple. Supposons que vous avez des interfaces voiture, avion, bateau. En fait, tout ce dont vous avez besoin est une classe qui envoie des personnes d’un endroit à un autre. Vous voulez que la façade décide quel véhicule utiliser. Ensuite, vous collectez toutes les références d’interface sous 1 parapluie et laissez-le décider / déléguer pour restr simple.

Head First: «Une façade simplifie non seulement une interface, mais elle dissocie un client d’un sous-système de composants. Les façades et les adaptateurs peuvent envelopper plusieurs classes, mais la façade est destinée à simplifier l’interface. ”

Tous les quatre modèles impliquent d’emballer un object / une classe interne avec un object extérieur, donc ils sont très similaires structurellement. Je décrirais la différence par le but:

  • Le proxy encapsule l’access de l’extérieur vers l’intérieur.
  • Le décorateur modifie ou étend le comportement de l’intérieur avec l’extérieur.
  • L’adaptateur convertit l’interface de l’intérieur vers l’extérieur.
  • Bridge sépare la partie invariable du comportement (externe) de la partie variable ou dépendante de la plate-forme (interne).

Et par variation d’interface entre les objects intérieurs et extérieurs:

  • dans les interfaces proxy sont les mêmes.
  • dans les interfaces Decorator sont les mêmes.
  • Dans Adapter, les interfaces sont différentes formellement, mais remplissent le même objective.
  • Dans Bridge, les interfaces sont conceptuellement différentes.

Je l’utilise assez souvent lorsque je consum des services Web. Le modèle de proxy devrait probablement être renommé en quelque chose de plus pragmatique, comme “Wrapper Pattern”. J’ai aussi une bibliothèque qui est un proxy pour MS Excel. Il est très facile d’automatiser Excel, sans avoir à se soucier de détails comme la version est installée (le cas échéant).

En parlant d’implémentation détaillée, je trouve une différence entre Proxy et Decorator, Adapter, Facade … Dans la mise en œuvre commune de ces patterns, il y a un object cible enveloppé par un object englobant. Le client utilise un object englobant au lieu d’un object cible. Et l’object cible joue effectivement un rôle important dans certaines méthodes d’enfermement d’objects.

Cependant, dans le cas de proxy, object contenant peut jouer certaines méthodes par lui-même, il suffit d’initialiser l’object cible lorsque le client appelle certaines méthodes auxquelles il a besoin d’un object cible. Il s’agit d’une initialisation paresseuse. Dans le cas d’autres modèles, l’object englobant est basé virtuellement sur l’object cible. Ainsi, l’object cible est toujours initialisé avec un object englobant dans les constructeurs / setters.

Une autre chose, un proxy fait exactement ce que fait une cible alors que d’autres modèles ajoutent plus de fonctionnalités à la cible.

Je voudrais append des exemples à la réponse de Bill Karwing (ce qui est génial btw.) J’ajoute également quelques différences clés d’implémentation, qui me semblent manquantes

Les parties citées proviennent de la réponse de [ https://stackoverflow.com/a/350471/1984346%5D (Bill Karwing)

Proxy, Decorator, Adapter et Bridge sont toutes des variantes de “wrapping” d’une classe. Mais leurs utilisations sont différentes.

  • Le proxy peut être utilisé lorsque vous souhaitez instancier un object paresseux ou masquer le fait que vous appelez un service distant ou contrôler l’access à l’object.

ProxyClass et ObjectClass qui est proxy, doivent implémenter la même interface, ils sont donc interchangeables

Exemple – object proxy coûteux

 class ProxyHumanGenome implements GenomeInterface { private $humanGenome = NULL; // humanGenome class is not instantiated at construct time function __construct() { } function getGenomeCount() { if (NULL == $this->humanGenome) { $this->instantiateGenomeClass(); } return $this->humanGenome->getGenomeCount(); } } class HumanGenome implement GenomeInterface { ... } 
  • Le décorateur est aussi appelé “Smart Proxy”. Ceci est utilisé lorsque vous souhaitez append des fonctionnalités à un object, mais pas en étendant le type de cet object. Cela vous permet de le faire lors de l’exécution.

DecoratorClass devrait (pourrait) implémenter une interface étendue d’ObjectClass. Ainsi, l’ObjectClass pourrait être remplacé par DecoratorClass, mais pas l’inverse.

Exemple – ajout de fonctionnalités supplémentaires

 class DecoratorHumanGenome implements CheckGenomeInterface { // ... same code as previous example // added functionality public function isComplete() { $this->humanGenome->getCount >= 21000 } } interface CheckGenomeInterface extends GenomeInterface { public function isComplete(); } class HumanGenome implement GenomeInterface { ... } 
  • L’adaptateur est utilisé lorsque vous disposez d’une interface abstraite et que vous souhaitez mapper cette interface à un autre object ayant un rôle fonctionnel similaire, mais une interface différente.

Différences d’implantation Proxy, Decorator, Adapter

L’adaptateur fournit une interface différente à son sujet. Le proxy fournit la même interface. Le décorateur fournit une interface améliorée.

  • Bridge est très similaire à Adapter, mais nous l’appelons Bridge lorsque vous définissez à la fois l’interface abstraite et l’implémentation sous-jacente. Si vous ne vous adaptez pas à un code hérité ou tiers, vous êtes le concepteur de tout le code, mais vous devez pouvoir échanger différentes implémentations.

  • Facade est une interface de niveau supérieur (read: simpler) avec un sous-système d’une ou plusieurs classes. Supposons que vous ayez un concept complexe qui nécessite plusieurs objects à représenter. Apporter des modifications à cet ensemble d’objects est déroutant, car vous ne savez pas toujours quel object a la méthode dont vous avez besoin d’appeler. C’est le moment d’écrire une façade qui fournit des méthodes de haut niveau pour toutes les opérations complexes que vous pouvez effectuer dans la collection d’objects. Exemple: un modèle de domaine pour une section scolaire, avec des méthodes telles que countStudents() , reportAttendance() , assignSubstituteTeacher() , etc.

La plupart des informations contenues dans cette réponse proviennent de https://sourcemaking.com/design_patterns , que je recommande comme une excellente ressource pour les modèles de conception.

Je crois que le code donnera des idées claires (pour compléter les autres réponses également). Veuillez voir ci-dessous, (Focalisez les types qu’une classe implémente et encapsule)

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestConsole { class Program { static void Main(ssortingng[] args) { /* Proxy */ Console.WriteLine(Environment.NewLine); Console.WriteLine("PROXY"); Console.WriteLine(Environment.NewLine); //instead of creating here create using a factory method, the facory method will return the proxy IReal realProxy = new RealProxy(); Console.WriteLine("calling do work with the proxy object "); realProxy.DoWork(); Console.WriteLine(Environment.NewLine); Console.WriteLine("ADAPTER"); Console.WriteLine(Environment.NewLine); /*Adapter*/ IInHand objectIHave = new InHand(); Api myApi = new Api(); //myApi.SomeApi(objectIHave); /*I cant do this, use a adapter then */ IActual myAdaptedObject = new ActualAdapterForInHand(objectIHave); Console.WriteLine("calling api with my adapted obj"); myApi.SomeApi(myAdaptedObject); Console.WriteLine(Environment.NewLine); Console.WriteLine("DECORATOR"); Console.WriteLine(Environment.NewLine); /*Decorator*/ IReady maleReady = new Male(); Console.WriteLine("now male is going to get ready himself"); maleReady.GetReady(); Console.WriteLine(Environment.NewLine); IReady femaleReady = new Female(); Console.WriteLine("now female is going to get ready her self"); femaleReady.GetReady(); Console.WriteLine(Environment.NewLine); IReady maleReadyByBeautician = new Beautician(maleReady); Console.WriteLine("now male is going to get ready by beautician"); maleReadyByBeautician.GetReady(); Console.WriteLine(Environment.NewLine); IReady femaleReadyByBeautician = new Beautician(femaleReady); Console.WriteLine("now female is going to get ready by beautician"); femaleReadyByBeautician.GetReady(); Console.WriteLine(Environment.NewLine); Console.ReadLine(); } } /*Proxy*/ public interface IReal { void DoWork(); } public class Real : IReal { public void DoWork() { Console.WriteLine("real is doing work "); } } public class RealProxy : IReal { IReal real = new Real(); public void DoWork() { real.DoWork(); } } /*Adapter*/ public interface IActual { void DoWork(); } public class Api { public void SomeApi(IActual actual) { actual.DoWork(); } } public interface IInHand { void DoWorkDifferently(); } public class InHand : IInHand { public void DoWorkDifferently() { Console.WriteLine("doing work slightly different "); } } public class ActualAdapterForInHand : IActual { IInHand hand = null; public ActualAdapterForInHand() { hand = new InHand(); } public ActualAdapterForInHand(IInHand hnd) { hand = hnd; } public void DoWork() { hand.DoWorkDifferently(); } } /*Decorator*/ public interface IReady { void GetReady(); } public class Male : IReady { public void GetReady() { Console.WriteLine("Taking bath.. "); Console.WriteLine("Dress up...."); } } public class Female : IReady { public void GetReady() { Console.WriteLine("Taking bath.. "); Console.WriteLine("Dress up...."); Console.WriteLine("Make up...."); } } //this is a decorator public class Beautician : IReady { IReady ready = null; public Beautician(IReady rdy) { ready = rdy; } public void GetReady() { ready.GetReady(); Console.WriteLine("Style hair "); if (ready is Female) { for (int i = 1; i <= 10; i++) { Console.WriteLine("doing ready process " + i); } } } } } 

Le modèle de conception n’est pas mathématique, c’est une combinaison d’art et de génie logiciel. Il n’y a rien comme pour cette exigence, vous devez utiliser proxy, pont, etc. Les modèles de conception sont créés pour résoudre les problèmes. Si vous prévoyez un problème de conception, utilisez-le. Sur la base de votre expérience, vous découvrirez un problème spécifique, quel modèle utiliser. Si vous êtes bon dans les principes de conception solides, vous auriez implémenté un modèle de conception sans savoir que c’est un modèle. L’exemple le plus courant est celui des patrons de statergy et d’usine

Par conséquent, concentrez-vous davantage sur des principes solides, des principes de codage propres