web-dev-qa-db-fra.com

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

Je regardais le modèle de procuration et, pour moi, cela ressemble beaucoup aux modèles de décorateur, d'adaptateur et de pont. Est-ce que je comprends mal quelque chose? Quelle est la différence? Pourquoi utiliser le modèle de proxy par rapport aux autres? Comment les avez-vous utilisés dans le passé dans des projets réels?

377
Charles Graham

Proxy, Decorator, Adapter et Bridge sont toutes des variantes permettant d’envelopper une classe. Mais leurs utilisations sont différentes.

  • Proxy peut être utilisé lorsque vous souhaitez instancier paresseux un objet, ou masquer le fait que vous appelez un service distant, ou contrôler l'accès à l'objet.

  • Décorateur est aussi appelé "Smart Proxy". Ceci est utilisé lorsque vous souhaitez ajouter une fonctionnalité à un objet, mais pas en étendant le type de cet objet. Cela vous permet de le faire au moment de l'exécution.

  • Adapter est utilisé lorsque vous avez une interface abstraite et que vous souhaitez mapper cette interface vers un autre objet 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. C'est à dire. 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 (lecture: plus simple) avec un sous-système d'une ou plusieurs classes. Supposons que vous ayez un concept complexe nécessitant plusieurs objets à représenter. Apporter des modifications à cet ensemble d'objets est source de confusion, car vous ne savez pas toujours quel objet a la méthode à 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 sur la collection d'objets. Exemple: un modèle de domaine pour une section scolaire, avec des méthodes telles que countStudents(), reportAttendance(), assignSubstituteTeacher(), etc.

617
Bill Karwin

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

Telles sont leurs structures.

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

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

  • Bridge et Adaptateur tous deux pointent vers un type existant. Mais le pont pointe vers un type abstrait et l'adaptateur peut pointer vers un type concret. Le pont vous permettra d'associer l'implémentation au moment de l'exécution, contrairement à l'adaptateur.

184

Mon point de vue sur le sujet.

Les quatre modèles ont beaucoup en commun, ils sont parfois appelés de manière informelle des wrappers ou des modèles de wrapper. Tous utilisent la composition, encapsulant le sujet et déléguant l'exécution au sujet à un moment donné, mappent un appel de méthode à un autre. Ils évitent au client la nécessité de construire un objet différent et de copier toutes les données pertinentes. 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 plus lisible par les autres développeurs.

Adaptateur

Adaptateur adapte le sujet (adapté) à une interface différente. De cette façon, nous pouvons ajouter un objet à une collection de types nominalement différents.

Adapter n'expose que les méthodes pertinentes au client, peut restreindre toutes les autres, révélant les intentions d'utilisation pour des contextes particuliers, comme l'adaptation d'une bibliothèque externe, le rendant moins général et plus ciblé sur les besoins de nos applications. Les adaptateurs augmentent la lisibilité et l'auto-description de notre code.

Les adaptateurs protègent une équipe du code instable des autres équipes; un outil de sauvetage pour les équipes offshore ;-)

Le terme moins mentionné a pour but d’empêcher la classe de sujets de dépasser les annotations. Avec autant de frameworks basés sur des annotations, cela devient un usage plus important que jamais.

Adapter permet de contourner Java la limitation d'un seul héritage. Il peut combiner plusieurs adaptateurs sous une même enveloppe, ce qui donne une impression d'héritage multiple.

Code sage, l'adaptateur est "mince". Cela ne devrait pas ajouter beaucoup de code à la classe adaptee, à part simplement appeler la méthode adaptee et effectuer des conversions de données occasionnelles nécessaires pour effectuer de tels appels.

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

Décorateur

Decorator non seulement délègue, ne mappe pas seulement une méthode à une autre, ils en font plus, ils modifient le comportement de certaines méthodes de sujet, il peut décider de ne pas appeler de méthode du sujet, de déléguer à un autre objet, un objet d'assistance.

Les décorateurs ajoutent généralement des fonctionnalités (de manière transparente) aux objets enveloppés tels que la journalisation, le cryptage, le formatage ou la compression sur le sujet. 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 reste OutputStream et peut être utilisé en tant que tel. C'est une différence technique majeure par rapport aux adaptateurs.

Des exemples de manuels de la famille de décorateurs entiers se trouvent facilement dans JDK - the Java IO. Toutes les classes comme BufferedOutputStream , FilterOutputStream et ObjectOutputStream sont les décorateurs de OutputStream . Ils peuvent être superposés en couches, un décorateur étant à nouveau décoré, ce qui ajoute davantage de fonctionnalités.

Proxy

Le proxy n'est pas un wrapper typique. L'objet encapsulé, le sujet du 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 objet lourd créé à la demande ou d'un objet distant dans une machine virtuelle Java ou un noeud de réseau différent, voire d'un objet non Java, d'un composant en code natif. Il ne doit pas nécessairement être emballé ou délégué à un autre objet.

Les exemples les plus typiques sont les proxies distants, les initialiseurs d'objets lourds et les proxys d'accès.

  • Proxy distant - le sujet se trouve sur un serveur distant, sur une JVM différente ou même non Java système. Le proxy convertit les appels de méthode en appels RMI/REST/SOAP ou tout autre moyen nécessaire, protégeant le client de l'exposition à la technologie sous-jacente.

  • Proxy Lazy Load - Initialisez entièrement l'objet uniquement lors de la première utilisation ou de la première utilisation intensive.

  • Proxy d'accès - contrôle l'accès au sujet.

Façade

La façade est étroitement associée au principe de conception de la moindre connaissance (loi de Demeter). Facade est très similaire à Adapter. Ils enveloppent tous les deux, ils mappent un objet sur un autre, mais leur intention est différente. Façade aplatit la structure complexe d'un sujet, graphe d'objet complexe, simplifiant l'accès à une structure complexe.

La façade enveloppe une structure complexe, lui fournissant une interface plate. Cela évite que l'objet client soit exposé à des relations internes dans la structure du sujet, favorisant ainsi un couplage lâche.

Pont

Variante plus complexe du modèle Adaptateur où non seulement la mise en œuvre 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 dissocie l'adaptateur même de l'interface d'adaptation. Il augmente la complexité plus que tout autre motif d'emballage, appliquez-le avec précaution.

Différences de constructeurs

Les différences de modèle sont également évidentes quand on regarde leurs constructeurs.

  • Le proxy n'enveloppe pas un objet existant. Il n'y a pas de sujet dans le constructeur.

  • Decorator et Adaptateur envelopper un objet déjà existant, et tel est généralement
    fourni dans le constructeur.

  • Le constructeur Facade prend l’élément racine d’un graphe d’objet entier, sinon il ressemble à Adapter.

Exemple concret - Adaptateur JAXB Marshalling . Le but de cet adaptateur est de mapper une classe simple et plate sur une structure plus complexe requise en externe et d'éviter les classes "polluantes" avec des annotations excessives.

50
Espinosa

Il y a beaucoup de chevauchement dans beaucoup de modèles de GoF. Ils sont tous construits sur la puissance du polymorphisme et parfois, leurs intentions ne diffèrent vraiment que par elles-mêmes. (stratégie contre état)

Ma compréhension des motifs a été multipliée par 100 après la lecture de Head First Design Patterns .

Je le recommande fortement!

29
Dinah

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

Je vais décorer points clés.

Décorateur:

  1. Ajoute un comportement à un objet lors de l'exécution. L'héritage est la clé pour réaliser cette fonctionnalité, ce qui constitue à la fois un avantage et un inconvénient de ce modèle.
  2. Il modifie le comportement de l'interface.

par exemple. (avec chaînage): Java.io classes de paquetages liées à InputStream & OutputStream interfaces

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

Proxy:

  1. Utilisez-le pour une initialisation lente, l'amélioration des performances en mettant en cache l'objet et en contrôlant l'accès au client/appelant. Il peut fournir un comportement alternatif ou appeler un objet réel. Au cours de ce processus, il peut créer un nouvel objet.
  2. Contrairement à Decorator, qui permet de chaîner des objets, Proxy ne permet pas de chaîner.

par exemple.: Java.rmi classes de paquets.

Adapter:

  1. Il permet à deux interfaces non liées de travailler ensemble à travers les différents objets, jouant éventuellement le même rôle.
  2. Modifie l'interface d'origine.

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

Bridge:

  1. Cela permet aux abstractions et aux implémentations de varier indépendamment.
  2. Il utilise la composition sur l'héritage.

par exemple. Classes de collection dans Java.util. List implémenté par ArrayList.

Remarques principales:

  1. Adapter fournit une interface différente pour son sujet. Proxy fournit la même interface. Décorateur fournit une interface améliorée.
  2. Adapter modifie l'interface d'un objet, Décorateur augmente les responsabilités d'un objet.
  3. Décorateur et Proxy ont des buts différents mais des structures similaires
  4. Adapter fait fonctionner les choses après leur conception. Bridge les fait fonctionner avant de l'être.
  5. Bridge est conçu à l'avance pour permettre à l'abstraction et à la mise en oeuvre de varier indépendamment. Adapter est ajouté ultérieurement pour que les classes non liées fonctionnent ensemble
  6. Décorateur est conçu pour vous permettre d'ajouter des responsabilités aux objets sans sous-classement.

Consultez d'excellentes questions/articles sur la SE concernant des exemples de différents modèles de conception

Quand utiliser le motif de décorateur?

Quand utilisez-vous le modèle de pont? En quoi est-il différent du modèle d’adaptateur?

Différences entre les motifs de proxy et de décorateur

8
Ravindra babu

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

Les entrées et les discussions y sont assez détaillées et renvoient également à d’autres articles pertinents. En passant, le wiki c2 est excellent pour s’interroger sur les nuances entre différents modèles.

Pour résumer les entrées c2, je dirais un décorateur ajoute/modifie le comportement, mais un proxy a plus à voir avec le contrôle d'accès (instanciation paresseuse, accès distant, sécurité, etc.). Mais comme je l'ai dit, les lignes qui les séparent 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.

7

Citation de Head First Design Designs

Définitions appartient au livre. Les exemples m'appartiennent.

Décorateur - Ne modifie pas l'interface, mais ajoute des responsabilités. Supposons que vous avez une interface de voiture. Lorsque vous l'implémenterez pour différents modèles de voiture, vous devrez peut-être ajouter plus de responsabilité pour certains modèles. Comme a toit ouvrant, airbag etc ..

Adaptateur - Convertit une interface en une autre. Vous avez une interface de voiture et vous voudriez qu’elle se comporte comme une jeep. Donc, vous prenez la voiture, modifiez-la et transformez en une jeep. Puisqu'il ne s'agit pas d'une vraie jeep. Mais agit comme une jeep.

Façade - Simplifie l’interface. Supposons que vous ayez des interfaces voiture, avion, navire. En fait, tout ce dont vous avez besoin est une classe qui envoie les gens d'un endroit à un autre. Vous voulez que la façade décide quel véhicule utiliser. Ensuite, vous collectez toutes ces références d'interface sous 1 parapluie et laissez-le décider/déléguer pour qu'il reste simple.

Head First: "Une façade simplifie non seulement une interface, 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 a pour objectif de simplifier, tandis que l'adaptateur convertit l'interface en quelque chose de différent. "

3
Teoman shipahi

Les quatre modèles impliquent l'enveloppement d'objets/classes internes avec des objets externes, de sorte qu'ils sont très similaires structurellement. Je soulignerais la différence par le but:

  • Proxy encapsule l'accès entre externe et interne.
  • Décorateur modifie ou étend le comportement de l’intérieur avec l’extérieur.
  • Adapter 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 objets internes et externes:

  • dans Proxy les interfaces sont les mêmes.
  • dans Décorateur les interfaces sont les mêmes.
  • in Adapter les interfaces sont différentes formellement, mais remplissent le même objectif.
  • in Bridge les interfaces sont différentes conceptuellement.
2
Alexey

En parlant d’implémentation détaillée, je trouve une différence entre Proxy et Décorateur, Adaptateur, Façade ... Dans l’implémentation commune de ces motifs, il y a un objet cible entouré d’un objet qui l’entoure. Le client utilise un objet englobant au lieu d'un objet cible. Et l’objet cible joue en réalité un rôle important dans certaines méthodes d’objet englobant.

Cependant, dans le cas de proxy, l'objet englobant peut jouer certaines méthodes seul; il initialise simplement l'objet cible lorsque le client appelle certaines méthodes dont il a besoin. Cet initialisation est paresseuse. Dans le cas d'autres modèles, l'objet englobant est virtuellement basé sur l'objet cible. Donc, l'objet cible est toujours initialisé avec l'objet englobant dans les constructeurs/setters.

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

1
bnguyen82

Je l'utilise assez souvent lors de la consommation de services Web. Le modèle de proxy devrait probablement être renommé en quelque chose de plus pragmatique, tel que 'Modèle de wrapper ". J'ai également une bibliothèque qui est un proxy pour MS Excel. Il est très facile d'automatiser Excel, sans avoir à vous soucier des détails d'arrière-plan tels que la version est installée (le cas échéant).

1
hmcclungiii

J'aimerais ajouter des exemples à la réponse de Bill Karwing (ce qui est excellent en fait). J'ajoute également quelques différences majeures de mise en œuvre, qui me manquent

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

Proxy, Decorator, Adapter et Bridge sont toutes des variantes permettant d’envelopper une classe. Mais leurs utilisations sont différentes.

  • Le proxy peut être utilisé lorsque vous souhaitez instancier paresseux un objet, ou masquer le fait que vous appelez un service distant, ou que vous contrôlez l'accès au objet.

ProxyClass et ObjectClass qui sont mandatés, doivent implémenter la même interface, de sorte qu'ils sont interchangeables

Exemple - objet coûteux proxy

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 { ... }
  • Decorator est également appelé "Smart Proxy". Ceci est utilisé lorsque vous souhaitez ajouter une fonctionnalité à un objet, mais pas en étendant le type de cet objet. Cela vous permet de le faire au moment de l'exécution.

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

Exemple - ajout d'une fonctionnalité d'ajout

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 avez une interface abstraite et que vous souhaitez mapper cette interface sur un autre objet ayant un rôle fonctionnel similaire, mais une interface différente.

Différences d'implantation Proxy, Decorator, Adapter

Adapter fournit une interface différente à son sujet. Le proxy fournit la même interface. Decorator 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. C'est à dire. 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 (lecture: plus simple) menant à un sous-système d'une ou de plusieurs classes. Supposons que vous ayez un concept complexe nécessitant plusieurs objets à représenter. Apporter des modifications à cet ensemble d'objets est source de confusion, car vous ne savez pas toujours quel objet a la méthode à 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 sur la collection d'objets. Exemple: un modèle de domaine pour une section scolaire, avec des méthodes telles que countStudents(), reportAttendance(), assignSubstituteTeacher(), etc.

La plupart des informations de cette réponse proviennent de https://sourcemaking.com/design_patterns, que je recommande en tant que Excellente ressource pour les modèles de conception.

1
nakashu

Je crois que le code donnera une idée claire (pour compléter les réponses des autres). Veuillez voir ci-dessous, (Concentrez-vous sur les types qu'une classe implémente et enveloppe)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestConsole
{
    class Program
    {
        static void Main(string[] 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);
                }

            }
        }
    }

}
0
Abdul Kader Jeelani