adapter-Qualsiasi esempio reale di Adapter Pattern [chiuso]


85

Voglio dimostrare l'uso di Adapter Pattern al mio team. Ho letto molti libri e articoli online. Tutti citano un esempio utile per comprendere il concetto (Shape, Memory Card, Electronic Adapter ecc.), Ma non esiste un caso di studio reale.

Puoi condividere qualche caso di studio di Adapter Pattern?

ps ho provato a cercare domande esistenti su stackoverflow, ma non ho trovato la risposta, quindi pubblicandola come una nuova domanda. Se sai che c'è già una risposta per questo, reindirizza.


4
Bene, se vuoi provarlo. Dovresti avere un esempio già pronto nel tuo ambiente, in effetti diversi. Altrimenti perché vorresti provarlo?
Tony Hopkinson


1
@TonyHopkinson Lo scopo è rendere le persone consapevoli di questo modello di progettazione con un esempio reale.
AksharRoop

10
@AksharRoop. Design Pattern è pensato per essere una soluzione a un problema, non una soluzione alla ricerca di un problema. Il miglior esempio è quello nel tuo "mondo".
Tony Hopkinson

1
@TonyHopkinson Potrei aver usato un termine errato per dimostrare qui, ma quello che intendevo era spiegare il concetto di questo modello con un buon esempio. Sono d'accordo che dovrei trovare quello nel mio sistema ...
AksharRoop

Risposte:


77

Molti esempi di Adapter sono banali o non realistici ( Rectangle vs LegacyRectangle, Ratchet vs Socket , SquarePeg vs RoundPeg , Duck vs Turkey ). Peggio ancora, molti non mostrano più adattatori per adattatori diversi ( qualcuno ha citato Arrays.asList di Java come esempio del pattern dell'adattatore ). Adattare un'interfaccia di una sola classe per funzionare con un'altra sembra un debole esempio del pattern GoF Adapter. Questo modello utilizza l'ereditarietà e il polimorfismo, quindi ci si aspetterebbe che un buon esempio mostri più implementazioni di adattatori per adattati diversi .

Il miglior esempio che ho trovato si trova nel Capitolo 26 di Applicazione di UML e modelli: un'introduzione all'analisi orientata agli oggetti e alla progettazione e allo sviluppo iterativo (3a edizione) . Le seguenti immagini sono tratte dal materiale dell'istruttore fornito su un sito FTP per il libro.

Il primo mostra come un'applicazione può utilizzare più implementazioni (adattate) funzionalmente simili (ad es. Calcolatori delle imposte, moduli contabili, servizi di autorizzazione del credito, ecc.) Ma con API differenti. Vogliamo evitare l'hard-coding del nostro codice a livello di dominio per gestire i diversi modi possibili per calcolare le tasse, post vendita, autorizzare le richieste di carte di credito, ecc. Questi sono tutti moduli esterni che potrebbero variare e per i quali non possiamo modificare il codice. L'adattatore ci consente di eseguire l'hard-coding nell'adattatore, mentre il nostro codice a livello di dominio utilizza sempre la stessa interfaccia (l'interfaccia IW qualunqueAdapter).

Fig. 26.1

Non vediamo nella figura sopra gli adattati effettivi. Tuttavia, la figura seguente mostra come viene effettuata una chiamata polimorfica a postSale(...)nell'interfaccia IAccountingAdapter, che si traduce in una registrazione della vendita tramite SOAP a un sistema SAP.

Fig. 26.2


anche questo esempio che utilizza le sessioni è abbastanza buono (sebbene l'implementazione non sia completamente corretta, penso, usando la statica): community.sitepoint.com/t/phpunit-testing-cookies-and-sessions/…
Alejandro Moreno


51

Come trasformare un francese in una persona normale ...

 public interface IPerson
    {
        string Name { get; set; }
    }

    public interface IFrenchPerson
    {
        string Nom { get; set; }
    }

    public class Person : IPerson
    {
        public string Name { get; set; }
    }

    public class FrenchPerson : IFrenchPerson
    {
        public string Nom { get; set; }
    }

    public class PersonService
    {
        public void PrintName(IPerson person)
        {
            Debug.Write(person.Name);
        }
    }

    public class FrenchPersonAdapter : IPerson
    {
        private readonly IFrenchPerson frenchPerson;

        public FrenchPersonAdapter(IFrenchPerson frenchPerson)
        {
            this.frenchPerson = frenchPerson;
        }

        public string Name 
        {
            get { return frenchPerson.Nom; }
            set { frenchPerson.Nom = value; }
        }
    } 

Esempio

    var service = new PersonService();
    var person = new Person();
    var frenchPerson = new FrenchPerson();

    service.PrintName(person);
    service.PrintName(new FrenchPersonAdapter(frenchPerson));

20
Sono francese e mi sento insultato perché non mi consideri una persona reale. (JK)
ZeroUltimax

13
@ZeroUltimax Sono abbastanza sicuro che questo codice non verrà compilato in Quebec.
Fuhrmanator

1
Qualsiasi programmatore senza la conoscenza degli adattatori, avrebbe risolto facilmente il problema. In che modo la conoscenza della teoria degli adattatori aiuta a risparmiare tempo oa migliorare la soluzione? Il punto finale è usare una classe speciale, invece di usare solo un metodo?
Rowan Gontier

Cosa succede se non controlli l'interfaccia e devi adattare una delle tue classi a una libreria di terze parti? Ci sono molte altre buone ragioni che esulano dallo scopo di questa risposta.
CountZero

3
Questo è il singolo più divertente - e forse uno dei più accessibili - esempi di come utilizzare il pattern Adapter che abbia mai incontrato.
Mass Dot Net l'


13

Ecco un esempio che simula la conversione analog datain digit data.

Fornisce un adattatore che converte i dati con cifre in virgola mobile in dati binari, probabilmente non è utile nel mondo reale, aiuta solo a spiegare il concetto di modello di adattatore.


Codice

AnalogSignal.java

package eric.designpattern.adapter;

public interface AnalogSignal {
    float[] getAnalog();

    void setAnalog(float[] analogData);

    void printAnalog();
}

DigitSignal.java

package eric.designpattern.adapter;

public interface DigitSignal {
    byte[] getDigit();

    void setDigit(byte[] digitData);

    void printDigit();
}

FloatAnalogSignal.java

package eric.designpattern.adapter;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FloatAnalogSignal implements AnalogSignal {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private float[] data;

    public FloatAnalogSignal(float[] data) {
        this.data = data;
    }

    @Override
    public float[] getAnalog() {
        return data;
    }

    @Override
    public void setAnalog(float[] analogData) {
        this.data = analogData;
    }

    @Override
    public void printAnalog() {
        logger.info("{}", Arrays.toString(getAnalog()));
    }
}

BinDigitSignal.java

package eric.designpattern.adapter;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BinDigitSignal implements DigitSignal {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private byte[] data;

    public BinDigitSignal(byte[] data) {
        this.data = data;
    }

    @Override
    public byte[] getDigit() {
        return data;
    }

    @Override
    public void setDigit(byte[] digitData) {
        this.data = digitData;
    }

    @Override
    public void printDigit() {
        logger.info("{}", Arrays.toString(getDigit()));
    }
}

AnalogToDigitAdapter.java

package eric.designpattern.adapter;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * Adapter - convert analog data to digit data.
 * </p>
 * 
 * @author eric
 * @date Mar 8, 2016 1:07:00 PM
 */
public class AnalogToDigitAdapter implements DigitSignal {
    public static final float DEFAULT_THRESHOLD_FLOAT_TO_BIN = 1.0f; // default threshold,
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private AnalogSignal analogSignal;
    private byte[] digitData;
    private float threshold;
    private boolean cached;

    public AnalogToDigitAdapter(AnalogSignal analogSignal) {
        this(analogSignal, DEFAULT_THRESHOLD_FLOAT_TO_BIN);
    }

    public AnalogToDigitAdapter(AnalogSignal analogSignal, float threshold) {
        this.analogSignal = analogSignal;
        this.threshold = threshold;
        this.cached = false;
    }

    @Override
    public synchronized byte[] getDigit() {
        if (!cached) {
            float[] analogData = analogSignal.getAnalog();
            int len = analogData.length;
            digitData = new byte[len];

            for (int i = 0; i < len; i++) {
                digitData[i] = floatToByte(analogData[i]);
            }
        }

        return digitData;
    }

    // not supported, should set the inner analog data instead,
    @Override
    public void setDigit(byte[] digitData) {
        throw new UnsupportedOperationException();
    }

    public synchronized void setAnalogData(float[] analogData) {
        invalidCache();
        this.analogSignal.setAnalog(analogData);
    }

    public synchronized void invalidCache() {
        cached = false;
        digitData = null;
    }

    @Override
    public void printDigit() {
        logger.info("{}", Arrays.toString(getDigit()));
    }

    // float -> byte convert,
    private byte floatToByte(float f) {
        return (byte) (f >= threshold ? 1 : 0);
    }
}

Codice: caso di test

AdapterTest.java

package eric.designpattern.adapter.test;

import java.util.Arrays;

import junit.framework.TestCase;

import org.junit.Test;

import eric.designpattern.adapter.AnalogSignal;
import eric.designpattern.adapter.AnalogToDigitAdapter;
import eric.designpattern.adapter.BinDigitSignal;
import eric.designpattern.adapter.DigitSignal;
import eric.designpattern.adapter.FloatAnalogSignal;

public class AdapterTest extends TestCase {
    private float[] analogData = { 0.2f, 1.4f, 3.12f, 0.9f };
    private byte[] binData = { 0, 1, 1, 0 };
    private float[] analogData2 = { 1.2f, 1.4f, 0.12f, 0.9f };

    @Test
    public void testAdapter() {
        AnalogSignal analogSignal = new FloatAnalogSignal(analogData);
        analogSignal.printAnalog();

        DigitSignal digitSignal = new BinDigitSignal(binData);
        digitSignal.printDigit();

        // adapter
        AnalogToDigitAdapter adAdapter = new AnalogToDigitAdapter(analogSignal);
        adAdapter.printDigit();
        assertTrue(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit()));

        adAdapter.setAnalogData(analogData2);
        adAdapter.printDigit();
        assertFalse(Arrays.equals(digitSignal.getDigit(), adAdapter.getDigit()));
    }
}

Dipendenza - tramite maven

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.13</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
    </dependency>

Come testare

Basta eseguire lo unit test.


7

Il modello dell'adattatore funziona come un ponte tra due interfacce incompatibili. Questo modello coinvolge una singola classe chiamata adattatore che è responsabile della comunicazione tra due interfacce indipendenti o incompatibili.

Esempi del mondo reale potrebbero essere un traduttore di lingue o un caricabatterie mobile. Altro qui in questo video di YouTube:

Youtube - Adapter Design pattern: Introduzione


4

È possibile utilizzare il modello di progettazione dell'adattatore quando si ha a che fare con interfacce diverse con un comportamento simile (che di solito significa classi con comportamento simile ma con metodi diversi). Un esempio potrebbe essere una classe per connettersi a una TV Samsung e un'altra per connettersi a una TV Sony. Condivideranno comportamenti comuni come aprire il menu, avviare la riproduzione, connettersi a una rete e così via, ma ogni libreria avrà un'implementazione diversa (con nomi di metodo e firme diversi). Queste diverse implementazioni specifiche del fornitore sono chiamate Adaptee nei diagrammi UML.

Quindi, nel tuo codice (chiamato Client nei diagrammi UML), invece del codice hard che il metodo chiama di ciascun fornitore (o Adaptee ), potresti creare un'interfaccia generica (chiamata Target nei diagrammi UML) per racchiudere questi comportamenti simili e lavorare con un solo tipo di oggetto.

Gli adattatori implementeranno quindi l' interfaccia di destinazione delegando le sue chiamate al metodo agli adattatori che vengono passati agli adattatori tramite il costruttore.

Affinché tu possa realizzarlo in codice Java, ho scritto un progetto molto semplice utilizzando esattamente lo stesso esempio menzionato sopra utilizzando adattatori per gestire più interfacce smart TV. Il codice è piccolo, ben documentato e autoesplicativo, quindi scavalo per vedere come sarebbe un'implementazione nel mondo reale.

Basta scaricare il codice e importarlo in Eclipse (o nel tuo IDE preferito) come progetto Maven. È possibile eseguire il codice eseguendo org.example.Main.java . Ricorda che la cosa importante qui è capire come le classi e le interfacce sono assemblate insieme per progettare il pattern. Ho anche creato alcuni falsi Adaptees nel pacchetto com.thirdparty.libs . Spero che sia d'aiuto!

https://github.com/Dannemann/java-design-patterns


3

I modelli di progettazione dell'adattatore aiutano a convertire l'interfaccia di una classe nell'interfaccia del cliente si aspetta.

Esempio: hai un servizio che restituisce il tempo (in gradi centigradi) passando il nome della città come valore di input. Ora, supponi che il tuo cliente voglia passare il codice postale come input e aspettarsi in cambio la temperatura della città. Qui è necessario un adattatore per ottenere questo risultato.

public interface IWetherFinder {
    public double getTemperature(String cityName);
}

class WeatherFinder implements IWetherFinder{
   @Override
   public double getTemperature(String cityName){
     return 40;
   }
}

interface IWeatherFinderClient
{
   public double getTemperature(String zipcode);
}  

public class WeatherAdapter implements IWeatherFinderClient {

    @Override
    public double getTemperature(String zipcode) {

        //method to get cityname by zipcode 
        String cityName = getCityName(zipcode);

        //invoke actual service
        IWetherFinder wetherFinder = new WeatherFinder();
        return wetherFinder.getTemperature(cityName);
    }

    private String getCityName(String zipCode) {
        return "Banaglore";
    }
}

2

Un vero esempio è Qt-Dbus.

Qt-dbus dispone di un'utilità per generare l'adattatore e il codice dell'interfaccia dal file xml fornito. Ecco i passaggi per farlo.

 1. Create the xml file - this xml file should have the interfaces 
that can be viewed by the qdbus-view in the system either on 
the system or session bus.

    2.With the utility - qdbusxml2cpp , you generate the interface adaptor code. 
This interface adaptor does the demarshalling of the data that is 
received from the client. After demarshalling, it invokes the 
user defined - custom methods ( we can say as adaptee).

    3. At the client side, we generate the interface from the xml file. 
This interface is invoked by the client. The interface does the 
marshalling of the data and invokes the adaptor interface. As told 
in the point number 2, the adaptor interface does the demarshalling 
and calls the adaptee - user defined methods.

Puoi vedere l'esempio completo di Qt-Dbus qui -

http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/


2

Puoi trovare un'implementazione PHP del pattern Adapter utilizzato come difesa dagli attacchi injection qui:

http://www.php5dp.com/category/design-patterns/adapter-composition/

Uno degli aspetti interessanti del pattern Adapter è che è disponibile in due versioni: un adattatore di classe che si basa su ereditarietà multipla e un adattatore di oggetti che si basa sulla composizione. L'esempio sopra si basa sulla composizione.



0

Un esempio reale può essere la segnalazione di documenti in un'applicazione. Codice semplice come qui.

Gli adattatori penso siano molto utili per la struttura di programmazione.

class WordAdaptee implements IReport{
    public void report(String s) {
        System.out.println(s +" Word");
    }
}

class ExcellAdaptee implements IReport{
    public void report(String s) {
        System.out.println(s +" Excel");
    }
}


class ReportAdapter implements IReport{
    WordAdaptee wordAdaptee=new WordAdaptee();
    @Override
    public void report(String s) {
        wordAdaptee.report(s);
    }
}

interface IReport {
    public void report(String s);
}

public class Main {
    public static void main(String[] args) {

        //create the interface that client wants
        IReport iReport=new ReportAdapter();

        //we want to write a report both from excel and world
        iReport.report("Trial report1 with one adaptee");  //we can directly write the report if one adaptee is avaliable 

        //assume there are N adaptees so it is like in our example
        IReport[] iReport2={new ExcellAdaptee(),new WordAdaptee()};

        //here we can use Polymorphism here  
        for (int i = 0; i < iReport2.length; i++) {
            iReport2[i].report("Trial report 2");
        }
    }
}

I risultati saranno:

Trial report1 with one adaptee Word
Trial report 2 Excel
Trial report 2 Word

1
Questo è in realtà un proxy. Un adattatore e un adattatore hanno interfacce diverse. Non implementano la stessa interfaccia. Questo è ciò che fa un proxy.
dvallejo

Questo non è il pattern Adapter. Il modello dell'adattatore viene utilizzato per implementare un'interfaccia di destinazione che l'adattatore non implementa.
FedericoG

0

Usa Adapter quando hai un'interfaccia che non puoi cambiare, ma che devi usare. Vedilo come se fossi il nuovo ragazzo in un ufficio e non puoi fare in modo che i capelli grigi seguano le tue regole: devi adattarti alle loro. Ecco un esempio reale di un progetto reale su cui ho lavorato qualche volta in cui l'interfaccia utente è un dato di fatto.

Hai un'applicazione che legge tutte le righe in un file in una struttura dati List e le visualizza in una griglia (chiamiamo l'interfaccia di archivio dati sottostante IDataStore). L'utente può navigare tra questi dati facendo clic sui pulsanti "Prima pagina", "Pagina precedente", "Pagina successiva", "Ultima pagina". Tutto funziona bene.

Ora l'applicazione deve essere utilizzata con i registri di produzione che sono troppo grandi per essere letti in memoria ma l'utente deve ancora navigare attraverso di essa! Una soluzione potrebbe essere quella di implementare una cache che memorizzi la prima pagina, la successiva, la precedente e l'ultima. Quello che vogliamo è che quando l'utente fa clic su "Pagina successiva", restituiamo la pagina dalla cache e aggiorniamo la cache; quando fanno clic sull'ultima pagina, torniamo all'ultima pagina dalla cache. In sottofondo abbiamo un filestream che fa tutta la magia. In questo modo abbiamo solo quattro pagine in memoria invece dell'intero file.

È possibile utilizzare un adattatore per aggiungere questa nuova funzionalità di cache all'applicazione senza che l'utente se ne accorga. Estendiamo l'attuale IDataStore e lo chiamiamo CacheDataStore. Se il file da caricare è grande, utilizziamo CacheDataStore. Quando effettuiamo una richiesta per la prima, la successiva, la precedente e l'ultima pagina, le informazioni vengono indirizzate alla nostra cache.

E chissà, domani il capo vuole iniziare a leggere i file da una tabella del database. Tutto quello che fai è ancora estendere IDataStore a SQLDataStore come hai fatto per Cache, impostare la connessione in background. Quando fanno clic su Pagina successiva, si genera la query SQL necessaria per recuperare le successive duecento righe dal database.

In sostanza, l'interfaccia originale dell'applicazione non è cambiata. Abbiamo semplicemente adattato le funzionalità moderne e interessanti per farlo funzionare preservando l'interfaccia legacy.


Non capisco? Sembra che tu abbia appena utilizzato un'interfaccia esistente e implementato i metodi? Dov'è la diversa interfaccia a cui devi adattarti e la classe dell'adattatore?
berimbolo

@berimbolo La tua confusione è valida poiché l'esempio sopra non parla chiaramente del pattern dell'adattatore.
rahil008

0

L'esempio di @Justice o non parla chiaramente del pattern dell'adattatore. Estendendo la sua risposta - Abbiamo un'interfaccia IDataStore esistente che utilizza il nostro codice consumer e non possiamo cambiarla. Ora ci viene chiesto di utilizzare una nuova fantastica classe dalla libreria XYZ che fa quello che vogliamo implementare, ma ma non possiamo cambiare quella classe per estendere il nostro IDataStore, visto già il problema? Creando una nuova classe - ADAPTER, che implementa l'interfaccia che il nostro codice consumatore si aspetta, cioè IDataStore e usando la classe della libreria di cui dobbiamo avere le caratteristiche - ADAPTEE, come membro del nostro ADAPTER, possiamo ottenere ciò che volevamo.



0

Un esempio dal framework Yii potrebbe essere: Yii utilizza la cache interna utilizzando un'interfaccia ICache. https://www.yiiframework.com/doc/api/1.1/ICache

la cui firma è come: -

abstract public boolean set(string $id, mixed $value, integer $expire=0, ICacheDependency $dependency=NULL)
abstract public mixed get(string $id)

Supponiamo che tu voglia usare all'interno di un progetto Yii la libreria cache di symfony https://packagist.org/packages/symfony/cache con la sua interfaccia cache, definendo questo servizio nella configurazione dei componenti dei servizi Yii (localizzatore di servizi) https: / /github.com/symfony/cache-contracts/blob/master/CacheInterface.php

    public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);

Vediamo, symfony cache ha un'interfaccia con solo un metodo get, manca un metodo set e una firma diversa per un metodo get, poiché Symfony usa il metodo get anche come setter quando fornisce il secondo parametro richiamabile.

Poiché il core di Yii utilizza internamente questa cache / interfaccia Yii, è difficile (estendendo Yii / YiiBase) se non impossibile in alcuni punti, riscrivere le chiamate a quell'interfaccia.

Inoltre la cache di Symfony non è né la nostra classe, quindi non possiamo riscrivere la sua interfaccia per adattarla all'interfaccia della cache di Yii.

Quindi ecco che arriva il modello dell'adattatore da salvare. Scriveremo una mappatura = un adattatore intermedio che mapperà le chiamate dell'interfaccia della cache Yii all'interfaccia della cache di Symfony

Assomiglierebbe a questo

    class YiiToSymfonyCacheAdapter implements \Yii\system\caching\ICache
    {
        private \Symfony\Contracts\Cache\CacheInterface $symfonyCache;

        public function __construct(\Symfony\Contracts\Cache\CacheInterface $symfonyCache)
        {
            $this->symfonyCache = $symfonyCache;
        }

      
      public boolean set(string $id, mixed $value, integer $expire=0, ICacheDependency 
       $dependency=NULL) 
      {

          // https://symfony.com/doc/current/cache.html
          return $this->symfonyCache->get(
              $id, 
              function($item) { 
              // some logic .. 
               return $value; 
              }
          );

//          https://github.com/symfony/cache/blob/master/Adapter/MemcachedAdapter.php
// if a class could be called statically, the adapter could call statically also eg. like this
//          return \Symfony\Component\Cache\Adapter\MemcacheAdapter::get(
//              $id, 
//              function($item) { 
//              // some logic .. 
//               return $value; 
//              }
          );
       }

       public mixed get(string $id) 
       {
           // https://github.com/symfony/cache/blob/master/Adapter/FilesystemAdapter.php 
           // if a class could be called statically, the adapter could call statically also eg. like this
           // \Symfony\Component\Cache\Adapter\FileSystemAdapter::get($id)
           return $this->symfonyCache->get($id) 
       }
    } 


-1

Questo è un esempio di implementazione dell'adattatore:

interface NokiaInterface {
    chargementNokia(x:boolean):void
}


class SamsungAdapter implements NokiaInterface {
//nokia chargement adapted to samsung
    chargementNokia(x:boolean){
        const old= new SamsungCharger();
        let y:number = x ? 20 : 1;
        old.charge(y);
      }
}


class SamsungCharger {
      charge(x:number){
            console.log("chrgement x ==>", x);
      }
}


function main() {
      //charge samsung with nokia charger
      const adapter = new SamsungAdapter();
      adapter.chargementNokia(true);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.