Domanda

Stavo guardando il modello Proxy e mi sembra molto simile ai modelli Decorator, Adapter e Bridge.Sto fraintendendo qualcosa?Qual è la differenza?Perché dovrei utilizzare il modello Proxy rispetto agli altri?Come li hai utilizzati in passato in progetti del mondo reale?

È stato utile?

Soluzione

Proxy, Decorator, Adapter e Bridge sono tutte varianti di " wrapping " una classe. Ma i loro usi sono diversi.

  • Proxy può essere utilizzato quando si desidera creare un'istanza pigra di un oggetto o nascondere il fatto che si sta chiamando un servizio remoto o controllare l'accesso all'oggetto.

  • Decoratore è anche chiamato " Smart Proxy. " Viene utilizzato quando si desidera aggiungere funzionalità a un oggetto, ma non estendendo il tipo di oggetto. Ciò ti consente di farlo in fase di esecuzione.

  • Adattatore viene utilizzato quando si dispone di un'interfaccia astratta e si desidera associare tale interfaccia a un altro oggetto che ha un ruolo funzionale simile, ma un'interfaccia diversa.

  • Bridge è molto simile a Adapter, ma lo chiamiamo Bridge quando definisci sia l'interfaccia astratta che l'implementazione sottostante. Cioè non ti stai adattando ad alcuni codici legacy o di terze parti, sei il progettista di tutto il codice ma devi essere in grado di scambiare diverse implementazioni.

  • Facciata è un'interfaccia di livello superiore (leggi: più semplice) verso un sottosistema di una o più classi. Supponiamo di avere un concetto complesso che richiede la rappresentazione di più oggetti. Apportare modifiche a quel set di oggetti è confuso, perché non si sa sempre quale oggetto ha il metodo che è necessario chiamare. Questo è il momento di scrivere una facciata che fornisce metodi di alto livello per tutte le complesse operazioni che è possibile eseguire per la raccolta di oggetti. Esempio: un modello di dominio per una sezione della scuola, con metodi come countStudents(), reportAttendance(), assignSubstituteTeacher() e così via.

Altri suggerimenti

Come dice la risposta di Bill, i loro casi d'uso sono diversi .

Così sono le loro strutture.

  • Proxy e Decorator hanno entrambi la stessa interfaccia dei loro tipi di wrapping, ma il proxy crea un'istanza sotto il cofano, mentre il decoratore prende un'istanza in il costruttore.

  • Adattatore e Facciata hanno entrambi un'interfaccia diversa da quella che avvolgono. Ma l'adattatore deriva da un'interfaccia esistente, mentre la facciata crea una nuova interfaccia.

  • Bridge e Adapter indicano entrambi un tipo esistente. Ma il bridge punterà a un tipo astratto e l'adattatore potrebbe puntare a un tipo concreto. Il bridge ti consentirà di accoppiare l'implementazione in fase di esecuzione, mentre l'adattatore di solito non lo farà.

La mia opinione sull'argomento.

Tutti e quattro i modelli hanno molto in comune, tutti e quattro sono talvolta chiamati informalmente wrapper o modelli wrapper.Tutti usano la composizione, avvolgendo l'oggetto e delegando l'esecuzione all'oggetto ad un certo punto, mappando una chiamata di metodo a un'altra.Risparmiano al cliente la necessità di dover costruire un oggetto diverso e copiare tutti i dati rilevanti.Se usati saggiamente, risparmiano memoria e processore.

Promuovendo l'accoppiamento libero rendono il codice, una volta stabile, meno esposto a inevitabili cambiamenti e più leggibile per gli altri sviluppatori.

Adattatore

L'adattatore adatta il soggetto (adattato) a un'interfaccia diversa.In questo modo possiamo aggiungere oggetti da posizionare in una raccolta di tipi nominalmente diversi.

L'adattatore espone solo i metodi rilevanti al client, può limitare tutti gli altri, rivelando intenti di utilizzo per contesti particolari, come adattare la libreria esterna, facendola apparire meno generale e più focalizzata sulle esigenze della nostra applicazione.Gli adattatori aumentano la leggibilità e l'autodescrizione del nostro codice.

Gli adattatori proteggono una squadra dal codice volatile di altre squadre;uno strumento salvavita quando si ha a che fare con team offshore ;-)

Lo scopo meno menzionato è quello di evitare che la classe dell'oggetto abbia un eccesso di annotazioni.Con così tanti framework basati sulle annotazioni, questo utilizzo diventa più importante che mai.

L'adattatore aiuta a aggirare la limitazione Java di una sola ereditarietà.Può combinare diversi adattati sotto un'unica busta dando l'impressione di eredità multipla.

Dal punto di vista del codice, l'adattatore è "sottile".Non dovrebbe aggiungere molto codice alla classe dell'adattato, oltre alla semplice chiamata del metodo dell'adattato e alle occasionali conversioni di dati necessarie per effettuare tali chiamate.

Non ci sono molti buoni esempi di adattatori in JDK o nelle librerie di base.Gli sviluppatori di applicazioni creano adattatori per adattare le librerie alle interfacce specifiche dell'applicazione.

Decoratore

Il decoratore non solo delega, non solo mappa un metodo a un altro, fa di più, modifica il comportamento di alcuni metodi del soggetto, può decidere di non chiamare affatto il metodo del soggetto, delegare a un oggetto diverso, un oggetto helper.

I decoratori in genere aggiungono (in modo trasparente) funzionalità all'oggetto avvolto come registrazione, crittografia, formattazione o compressione nell'oggetto.Questa nuova funzionalità potrebbe portare molto nuovo codice.Pertanto, i decoratori sono generalmente molto più “grassi” degli adattatori.

Decoratore deve essere una sottoclasse dell'interfaccia del soggetto.Possono essere utilizzati in modo trasparente al posto dei suoi argomenti.Vedi BufferedOutputStream, è ancora OutputStream e può essere utilizzato come tale.Questa è una grande differenza tecnica rispetto agli adattatori.

Esempi di libri di testo dell'intera famiglia di decoratori sono facilmente disponibili in JDK, Java IO.A tutte le classi piace BufferedOutputStream, FilterOutputStream E OggettoOutputStream sono decoratori di OutputStream.Possono essere stratificati a cipolla, dove un decoratore viene nuovamente decorato, aggiungendo più funzionalità.

Procura

Il proxy non è un wrapper tipico.L'oggetto avvolto, l'oggetto del proxy, potrebbe non esistere ancora al momento della creazione del proxy.Il proxy spesso lo crea internamente.Potrebbe trattarsi di un oggetto pesante creato su richiesta, oppure di un oggetto remoto in una JVM diversa o in un nodo di rete diverso e persino un oggetto non Java, un componente in codice nativo.Non deve necessariamente avvolgere o delegare a un altro oggetto.

Gli esempi più tipici sono proxy remoti, inizializzatori di oggetti pesanti e proxy di accesso.

  • Proxy remoto: il soggetto è su server remoto, diversi JVM o persino un sistema non Java.Il proxy converte le chiamate al metodo in chiamate RMI/REST/SOAP o tutto ciò che è necessario, proteggendo il cliente dall'esposizione al sottostante Tecnologia.

  • Proxy di carico pigro: inizializza completamente l'oggetto solo il primo utilizzo o il primo utilizzo intensivo.

  • Proxy di accesso: controlla l'accesso al soggetto.

Facciata

La facciata è strettamente associata al principio di progettazione della minima conoscenza (legge di Demetra).La facciata è molto simile all'adattatore.Entrambi avvolgono, entrambi mappano un oggetto su un altro, ma differiscono nell'intento.La facciata appiattisce la struttura complessa di un soggetto, un oggetto grafico complesso, semplificando l'accesso a una struttura complessa.

La facciata avvolge una struttura complessa, fornendole un'interfaccia piatta.Ciò impedisce all'oggetto cliente di essere esposto a relazioni interne nella struttura del soggetto, promuovendo così un accoppiamento allentato.

Ponte

Variante più complessa del modello Adapter in cui varia non solo l'implementazione ma anche l'astrazione.Aggiunge un ulteriore riferimento indiretto alla delega.La delegazione extra è il ponte.Disaccoppia l'adattatore anche dall'adattamento dell'interfaccia.Aumenta la complessità più di qualsiasi altro modello di avvolgimento, quindi applicalo con cura.

Differenze nei costruttori

Le differenze di modello sono evidenti anche guardando i loro costruttori.

  • Procura non sta avvolgendo un oggetto esistente.Non c'è soggetto nel costruttore.

  • Decoratore E Adattatore avvolge l'oggetto già esistente, e questo è in genere
    fornito nel costruttore.

  • Facciata Il costruttore prende l'elemento radice di un grafico a oggetti intero, altrimenti sembra lo stesso dell'adattatore.

Esempio di vita reale – Adattatore di marshalling JAXB.Lo scopo di questo adattatore è la mappatura di una semplice classe flat su una struttura più complessa richiesta esternamente e per evitare che la classe dell'oggetto "inquini" con annotazioni eccessive.

Esistono molte sovrapposizioni in molti dei modelli GoF. Sono tutti costruiti sul potere del polimorfismo e talvolta differiscono solo per l'intento. (strategia vs. stato)

La mia comprensione dei motivi è aumentata di 100 volte dopo aver letto Head First Design Patterns .

Lo consiglio vivamente!

Tutte le buone risposte degli esperti hanno già spiegato cosa significa ogni modello.

Farò decorare punti chiave.

Decorator:

  1. Aggiungi comportamento all'oggetto in fase di esecuzione . L'ereditarietà è la chiave per ottenere questa funzionalità, che è sia un vantaggio che uno svantaggio di questo modello.
  2. Modifica il comportamento dell'interfaccia.

es. (con concatenamento): java.io classi di pacchetti relative a InputStream & amp; OutputStream interfacce

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

Proxy:

  1. Utilizzalo per l'inizializzazione lenta, il miglioramento delle prestazioni memorizzando nella cache l'oggetto e controllando l'accesso al client / chiamante . Può fornire un comportamento alternativo o chiamare un oggetto reale. Durante questo processo, può creare un nuovo oggetto.
  2. A differenza di Decoratore , che consente il concatenamento di oggetti, il proxy non consente il concatenamento.

ad es .: java.rmi classi di pacchetti.

Adapter:

  1. Permette a due interfacce non correlate di lavorare insieme attraverso i diversi oggetti , possibilmente giocando lo stesso ruolo.
  2. Modifica l'interfaccia originale .

es. java.io.InputStreamReader (Reader restituisce un java.util)

Ponte:

  1. Permette alle astrazioni e alle implementazioni di variare indipendentemente .
  2. Utilizza composizione per ereditarietà .

es. Classi di raccolta in List. ArrayList implementato da <=>.

Note chiave:

  1. Adapter fornisce un'interfaccia diversa rispetto al soggetto. Proxy fornisce la stessa interfaccia. Decorator fornisce un'interfaccia migliorata.
  2. Adapter cambia l'interfaccia di un oggetto, Decora- tore migliora le responsabilità di un oggetto.
  3. Decorator e Proxy hanno scopi diversi ma strutture simili
  4. Adapter fa funzionare le cose dopo che sono state progettate; Bridge li fa funzionare prima che lo siano.
  5. Bridge è progettato in anticipo per consentire all'astrazione e all'implementazione di variare in modo indipendente. Adapter viene adattato per consentire alle classi non correlate di lavorare insieme
  6. Decorator è progettato per consentire di aggiungere responsabilità agli oggetti senza sottoclasse.

Dai un'occhiata alle grandi domande / articoli su SE riguardanti esempi di vari modelli di design

Quando utilizzare il motivo decorativo?

Quando usi il modello Bridge? In cosa differisce dal modello dell'adattatore?

Differenze tra Proxy e Decorator Pattern

Sono abbastanza simili e le linee tra loro sono abbastanza grigie. Ti suggerisco di leggere Proxy Pattern e Decorator Pattern voci nel wiki c2.

Le voci e le discussioni sono piuttosto estese e si collegano anche ad altri articoli pertinenti. A proposito, il wiki di c2 è eccellente quando ci si interroga sulle sfumature tra i diversi schemi.

Per riassumere le voci di c2, direi che un decoratore aggiunge / modifica il comportamento, ma un proxy ha più a che fare con il controllo degli accessi (istanza pigra, accesso remoto, sicurezza ecc.). Ma come ho detto, le linee tra loro sono grigie e vedo riferimenti a proxy che potrebbero essere facilmente visualizzati come decoratori e viceversa.

Questa è la citazione di Head First Design Patterns

Le definizioni appartengono al libro. Gli esempi mi appartengono.

Decoratore - Non & # 8217; non altera l'interfaccia, ma aggiunge responsabilità. Supponiamo di avere un'interfaccia per auto, quando lo implementi per diversi modelli dell'auto (s, sv, sl) potresti dover aggiungere più responsabilità per alcuni modelli. Come ha tetto apribile, airbag ecc.

Adattatore : converte un'interfaccia in un'altra. Hai un'interfaccia per auto e vorresti che si comportasse come una jeep. Quindi prendi la macchina, la modifichi e la trasformi in una jeep. Dal momento che non è una vera jeep. Ma si comporta come una jeep.

Facciata : semplifica l'interfaccia. Supponiamo di avere interfacce auto, aereo, nave. In realtà tutto ciò che serve è una classe che manda le persone da una posizione all'altra. Vuoi che la facciata decida quale veicolo usare. Quindi raccogli tutti quei riferimenti all'interfaccia sotto un unico ombrello e lasci che decida / deleghi per mantenerlo semplice.

Head First: " Una facciata non solo semplifica un'interfaccia, ma disaccoppia un client da un sottosistema di componenti. Le facciate e gli adattatori possono avvolgere più classi, ma l'intento di una facciata è semplificare, mentre un adattatore & # 8217; s è per convertire l'interfaccia in qualcosa di diverso. "

Tutti e quattro i modelli implicano un avvolgimento di oggetto / classe interni con uno esterno, quindi sono molto simili strutturalmente. Vorrei delineare la differenza in base allo scopo:

  • Proxy incapsula l'accesso da esterno a interno.
  • Decoratore modifica o estende il comportamento di interno con esterno.
  • Adattatore converte l'interfaccia da interna a esterna.
  • Bridge separa la parte invariabile del comportamento (esterna) dalla parte variabile o dipendente dalla piattaforma (interna).

E per variazione dell'interfaccia tra oggetti interni ed esterni:

    Le interfacce
  • in Proxy sono uguali.
  • Le interfacce
  • in Decorator sono le stesse.
  • Le interfacce
  • in Adattatore sono formalmente diverse, ma soddisfano lo stesso scopo.
  • Le interfacce
  • in Bridge sono concettualmente diverse.

Lo uso abbastanza spesso quando utilizzo i servizi web. Il Proxy Pattern dovrebbe probabilmente essere rinominato in qualcosa di più pragmatico, come 'Wrapper Pattern & Quot ;. Ho anche una libreria che è un proxy per MS Excel. Semplifica l'automazione di Excel, senza doversi preoccupare dei dettagli di background come la versione installata (se presente).

Parlando dell'implementazione dei dettagli, trovo una differenza tra Proxy e Decorator, Adapter, Facade ... Nell'implementazione comune di questi pattern c'è un oggetto target avvolto da un oggetto che lo racchiude. Il client utilizza l'oggetto che racchiude anziché l'oggetto di destinazione. E l'oggetto target svolge effettivamente un ruolo importante all'interno di alcuni dei metodi per racchiudere l'oggetto.

Tuttavia, nel caso del proxy, racchiudere l'oggetto può riprodurre alcuni metodi da solo, inizializza l'oggetto target quando il client chiama alcuni metodi a cui è necessario prendere parte l'oggetto target. Questa è l'inizializzazione lazy. Nel caso di altri motivi, racchiudere l'oggetto è praticamente basato sull'oggetto target. Quindi l'oggetto target viene sempre inizializzato insieme a racchiudere l'oggetto in costruttori / setter.

Un'altra cosa, un proxy fa esattamente quello che fa un bersaglio mentre altri schemi aggiungono più funzionalità al bersaglio.

Vorrei aggiungere esempi alla risposta di Bill Karwing (che è eccezionale tra l'altro). Aggiungo anche alcune differenze chiave di implementazione, che ritengo manchino

Le parti tra virgolette provengono dalla risposta di [ https://stackoverflow.com/a/350471/1984346] ( Bill Karwing)

  

Proxy, Decorator, Adapter e Bridge sono tutte varianti di " wrapping " una classe.   Ma i loro usi sono diversi.

     
      
  • Proxy può essere utilizzato quando si desidera creare un'istanza pigra di un oggetto oppure   nascondere il fatto che si sta chiamando un servizio remoto o controllare l'accesso   all'oggetto.
  •   

ProxyClass e ObjectClass che sono in proxy, dovrebbero implementare la stessa interfaccia, quindi sono intercambiabili

Esempio: oggetto costoso 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 { ... }
  
      
  • Decoratore è anche chiamato " Smart Proxy. " Questo è usato quando vuoi   aggiungere funzionalità a un oggetto, ma non estendendolo   genere. Ciò ti consente di farlo in fase di esecuzione.
  •   

DecoratorClass dovrebbe (potrebbe) implementare l'interfaccia estesa di ObjectClass. Quindi ObjectClass potrebbe essere sostituito da DecoratorClass, ma non viceversa.

Esempio: aggiunta della funzionalità di aggiunta

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 { ... }
  
      
  • Adattatore viene utilizzato quando si dispone di un'interfaccia astratta e si desidera   associa quell'interfaccia a un altro oggetto con funzioni simili   ruolo, ma un'interfaccia diversa.
  •   

Differenze di implosione Proxy, Decorator, Adapter

L'adattatore fornisce un'interfaccia diversa rispetto al soggetto. Il proxy fornisce la stessa interfaccia. Decorator fornisce un'interfaccia migliorata.

  
      
  • Bridge è molto simile all'adapter, ma quando lo chiamiamo bridge   definire sia l'interfaccia astratta che l'implementazione sottostante.   Cioè non ti stai adattando ad alcuni codici legacy o di terze parti, lo sei   il progettista di tutto il codice ma devi essere in grado di scambiarlo   diverse implementazioni.

  •   
  • Facciata è un'interfaccia di livello superiore (leggi: più semplice) per un sottosistema di   una o più classi. Supponiamo di avere un concetto complesso che richiede   più oggetti da rappresentare. Apportare modifiche a quel set di oggetti   è confuso, perché non sai sempre quale oggetto ha   metodo che devi chiamare. Questo è il momento di scrivere una facciata   fornisce metodi di alto livello per tutte le operazioni complesse che è possibile eseguire   alla raccolta di oggetti. Esempio: un modello di dominio per una scuola   sezione, con metodi come countStudents(), reportAttendance(),   assignSubstituteTeacher() e così via.

  •   

La maggior parte delle informazioni contenute in questa risposta proviene da https://sourcemaking.com/design_patterns , che raccomando come risorsa eccellente per i modelli di progettazione.

Credo che il codice fornirà idee chiare (per integrare anche le risposte degli altri). Di seguito, vedi (Concentrati sui tipi che una classe implementa e racchiude)

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);
                }

            }
        }
    }

}

Il modello di progettazione non è matematica, è una combinazione di arte e ingegneria del software. Non c'è niente di simile per questo requisito che è necessario utilizzare proxy, bridge, ecc. Vengono creati modelli di progettazione per risolvere i problemi. Se prevedi un problema di progettazione, utilizzalo. In base all'esperienza, imparerai a conoscere il problema specifico, quale modello utilizzare. Se sei bravo nei solidi principi di progettazione, avresti implementato il modello di progettazione senza sapere che è modello. Esempio comune è la statergia e i modelli di fabbrica

Quindi concentrarsi maggiormente su solidi principi desighn, principi di codifica puliti e ttd

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top