adaptador: cualquier ejemplo real de patrón de adaptador [cerrado]


84

Quiero demostrarle a mi equipo el uso de Adapter Pattern . He leído muchos libros y artículos en línea. Todo el mundo está citando un ejemplo que es útil para entender el concepto (forma, tarjeta de memoria, adaptador electrónico, etc.), pero no hay un caso de estudio real.

¿Puede compartir algún estudio de caso de Adapter Pattern?

ps Intenté buscar preguntas existentes en stackoverflow, pero no encontré la respuesta, así que publiqué como una nueva pregunta. Si sabe que ya hay una respuesta para esto, redirija.


4
Bueno, si quieres probarlo. Debería tener un ejemplo listo para usar en su entorno, de hecho varios. De lo contrario, ¿por qué querría hacer una demostración?
Tony Hopkinson

1
r4.

1
@TonyHopkinson El objetivo es que la gente conozca este patrón de diseño con un ejemplo real.
AksharRoop

10
@AksharRoop. Design Pattern está destinado a ser una solución a un problema, no una solución en busca de un problema. El mejor ejemplo es uno en su propio "mundo".
Tony Hopkinson

1
@TonyHopkinson Puede que haya usado un término incorrecto para demostrar aquí, pero lo que quise decir fue explicar el concepto de este patrón con un buen ejemplo. Estoy de acuerdo en que debería encontrar uno en mi propio sistema ...
AksharRoop

Respuestas:


77

Muchos ejemplos de Adapter son triviales o poco realistas ( Rectangle vs. LegacyRectangle, Ratchet vs. Socket , SquarePeg vs. RoundPeg , Duck vs. Turkey ). Peor aún, muchos no muestran múltiples adaptadores para diferentes adaptadores ( alguien citó Arrays.asList de Java como un ejemplo del patrón de adaptador ). Adaptar una interfaz de una sola clase para trabajar con otra parece un ejemplo débil del patrón del adaptador GoF. Este patrón usa herencia y polimorfismo, por lo que uno esperaría un buen ejemplo para mostrar múltiples implementaciones de adaptadores para diferentes adaptadores .

El mejor ejemplo que encontré está en el Capítulo 26 de Aplicación de UML y patrones: Introducción al análisis y diseño orientado a objetos y al desarrollo iterativo (3ª edición) . Las siguientes imágenes son del material del instructor proporcionado en un sitio FTP para el libro.

El primero muestra cómo una aplicación puede usar múltiples implementaciones (adaptadas) que son funcionalmente similares (por ejemplo, calculadoras de impuestos, módulos de contabilidad, servicios de autorización de crédito, etc.) pero tienen diferentes API. Queremos evitar codificar nuestro código de capa de dominio para manejar las diferentes formas posibles de calcular impuestos, publicar ventas, autorizar solicitudes de tarjetas de crédito, etc. Todos estos son módulos externos que pueden variar y para los cuales no podemos modificar código. El adaptador nos permite hacer la codificación en el adaptador, mientras que nuestro código de capa de dominio siempre usa la misma interfaz (la interfaz IWhateverAdapter).

Figura 26.1

No vemos en la figura anterior los adaptados reales. Sin embargo, la siguiente figura muestra cómo se realiza una llamada polimórfica a postSale(...)en la interfaz IAccountingAdapter, lo que da como resultado una publicación de la venta mediante SOAP en un sistema SAP.

Figura 26.2


este ejemplo usando sesiones también es bastante bueno (aunque la implementación no es del todo correcta, creo, usando estática): community.sitepoint.com/t/phpunit-testing-cookies-and-sessions/…
Alejandro Moreno


50

Cómo convertir a un francés en una persona normal ...

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

Ejemplo

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

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

19
Soy francesa y me siento insultada de que no me consideres una persona real. (JK)
ZeroUltimax

13
@ZeroUltimax Estoy bastante seguro de que este código no se compilará en Quebec.
Fuhrmanator

Cualquier codificador sin conocimientos de adaptadores, habría resuelto el problema fácilmente. ¿Cómo ayuda el conocimiento de la teoría del adaptador a ahorrar tiempo oa mejorar la solución? ¿El punto final es usar una clase especial, en lugar de usar solo un método?
Rowan Gontier

¿Qué sucede si no controla la interfaz y necesita adaptar una de sus clases a una biblioteca de terceros? Muchas otras buenas razones que están fuera del alcance de esta respuesta.
CountZero

3
Este es el ejemplo más divertido, y posiblemente uno de los más accesibles, de cómo usar el patrón Adaptador con el que me he encontrado.
Mass Dot Net


13

Aquí hay un ejemplo que simula la conversión analog dataa digit data.

Proporciona un adaptador que convierte datos de dígitos flotantes en datos binarios, probablemente no sea útil en el mundo real, solo ayuda a explicar el concepto de patrón de adaptador.


Código

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

Código - Caso de prueba

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

Dependencia - vía 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>

Cómo probar

Simplemente ejecute la prueba unitaria.


7

El patrón de adaptador funciona como un puente entre dos interfaces incompatibles. Este patrón involucra una sola clase llamada adaptador que es responsable de la comunicación entre dos interfaces independientes o incompatibles.

Los ejemplos del mundo real pueden ser un traductor de idiomas o un cargador de móvil. Más aquí en este video de youtube:

Youtube - Patrón de diseño del adaptador: Introducción


3

Puede usar el patrón de diseño del Adaptador cuando tenga que lidiar con diferentes interfaces con un comportamiento similar (lo que generalmente significa clases con un comportamiento similar pero con diferentes métodos). Un ejemplo de ello sería una clase para conectarse a un televisor Samsung y otra para conectar a un televisor Sony. Compartirán un comportamiento común como abrir el menú, iniciar la reproducción, conectarse a una red, etc., pero cada biblioteca tendrá una implementación diferente (con diferentes nombres de métodos y firmas). Estas diferentes implementaciones específicas de proveedores se denominan Adaptee en los diagramas UML.

Entonces, en su código (llamado Cliente en los diagramas UML), en lugar de codificar las llamadas al método de cada proveedor (o Adaptee ), puede crear una interfaz genérica (llamada Destino en los diagramas UML) para envolver estos comportamientos similares y trabajar con un solo tipo de objeto.

Los adaptadores implementarán la interfaz de destino delegando sus llamadas de método a los adaptadores que se pasan a los adaptadores a través del constructor.

Para que se dé cuenta de esto en el código Java, escribí un proyecto muy simple usando exactamente el mismo ejemplo mencionado anteriormente usando adaptadores para manejar múltiples interfaces de TV inteligente. El código es pequeño, está bien documentado y se explica por sí mismo, así que indague en él para ver cómo se vería una implementación en el mundo real.

Simplemente descargue el código e impórtelo a Eclipse (o su IDE favorito) como un proyecto de Maven. Puede ejecutar el código ejecutando org.example.Main.java . Recuerde que lo importante aquí es comprender cómo se ensamblan las clases y las interfaces para diseñar el patrón. También creé algunos Adaptees falsos en el paquete com.thirdparty.libs . ¡Espero eso ayude!

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


2

Un ejemplo real es Qt-Dbus.

El qt-dbus tiene una utilidad para generar el adaptador y el código de interfaz a partir del archivo xml proporcionado. Estos son los pasos para hacerlo.

 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.

Puede ver el ejemplo completo de Qt-Dbus aquí -

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


2

Puede encontrar una implementación PHP del patrón Adaptador utilizado como defensa contra ataques de inyección aquí:

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

Uno de los aspectos interesantes del patrón Adaptador es que viene en dos versiones: un adaptador de clase que depende de la herencia múltiple y un adaptador de objetos que depende de la composición. El ejemplo anterior se basa en la composición.



2

Los patrones de diseño del adaptador ayudan a convertir la interfaz de una clase en la interfaz que espera el cliente.

Ejemplo: tiene un servicio que devuelve el tiempo (en grados Celsius) pasando el nombre de la ciudad como valor de entrada. Ahora, suponga que su cliente quiere pasar el código postal como entrada y espera la temperatura de la ciudad a cambio. Aquí necesitas un adaptador para lograrlo.

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

0

Un ejemplo real puede ser la presentación de informes de documentos en una aplicación. Código simple como aquí.

Los adaptadores creo que son muy útiles para la estructura de programación.

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

Los resultados serán:

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

1
Este es en realidad un proxy. Un adaptador y un adaptee tienen diferentes interfaces. No implementan la misma interfaz. Eso es lo que hace un proxy.
dvallejo

Este no es el patrón de Adaptador. El patrón de adaptador se usa para implementar una interfaz de destino que el adaptado no implementa.
FedericoG

0

Utilice el Adaptador cuando tenga una interfaz que no pueda cambiar, pero que necesite utilizar. Míralo como si fueras el chico nuevo en una oficina y no puedes hacer que las canas sigan tus reglas, debes adaptarte a las de ellos. Aquí hay un ejemplo real de un proyecto real en el que trabajé en algún momento donde la interfaz de usuario es un hecho.

Tiene una aplicación que lee todas las líneas de un archivo en una estructura de datos de lista y las muestra en una cuadrícula (llamemos a la interfaz del almacén de datos subyacente IDataStore). El usuario puede navegar a través de estos datos haciendo clic en los botones "Primera página", "Página anterior", "Página siguiente", "Última página". Todo funciona bien.

Ahora la aplicación debe usarse con registros de producción que son demasiado grandes para leerlos en la memoria, ¡pero el usuario aún necesita navegar a través de ellos! Una solución sería implementar una caché que almacena la primera página, la siguiente, la anterior y la última. Lo que queremos es que cuando el usuario haga clic en "Página siguiente", devolvemos la página del caché y actualizamos el caché; cuando hacen clic en la última página, devolvemos la última página de la caché. En el fondo tenemos un flujo de archivos haciendo toda la magia. Al hacerlo, solo tenemos cuatro páginas en la memoria en comparación con el archivo completo.

Puede usar un adaptador para agregar esta nueva función de caché a su aplicación sin que el usuario se dé cuenta. Extendemos el IDataStore actual y lo llamamos CacheDataStore. Si el archivo a cargar es grande, usamos CacheDataStore. Cuando solicitamos las páginas Primera, Siguiente, Anterior y Última, la información se envía a nuestra caché.

Y quién sabe, mañana el jefe quiere empezar a leer los archivos de una tabla de base de datos. Todo lo que debe hacer es extender IDataStore a SQLDataStore como lo hizo con Cache, configurar la conexión en segundo plano. Cuando hacen clic en Página siguiente, genera la consulta SQL necesaria para obtener los siguientes doscientos filas de la base de datos.

Esencialmente, la interfaz original de la aplicación no cambió. Simplemente adaptamos características modernas y geniales para que funcionen mientras preservamos la interfaz heredada.


¿No entiendo? ¿Parece que acaba de utilizar una interfaz existente e implementó los métodos? ¿Dónde está la interfaz diferente a la que necesita adaptarse y la clase de adaptador?
berimbolo

@berimbolo Tu confusión es válida ya que el ejemplo anterior no habla claramente del patrón del adaptador.
rahil008

0

El ejemplo de @Justice o no habla claramente del patrón del adaptador. Ampliando su respuesta: tenemos una interfaz IDataStore existente que utiliza nuestro código de consumidor y no podemos cambiarla. Ahora se nos pide que usemos una nueva clase genial de la biblioteca XYZ que hace lo que queremos implementar, pero pero, no podemos cambiar esa clase para extender nuestro IDataStore, ¿ya ha visto el problema? Creando una nueva clase - ADAPTADOR, que implementa la interfaz que espera nuestro código de consumidor, es decir IDataStore y usando la clase de la biblioteca cuyas características necesitamos tener - ADAPTEE, como miembro de nuestro ADAPTADOR, podemos lograr lo que queríamos.



0

Un ejemplo del marco de Yii sería: Yii usa caché internamente utilizando una interfaz ICache. https://www.yiiframework.com/doc/api/1.1/ICache

cuya firma es como: -

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

Digamos que le gustaría usar dentro de un proyecto Yii la biblioteca de caché de Symfony https://packagist.org/packages/symfony/cache con su interfaz de caché, definiendo este servicio en la configuración de componentes de servicios de Yii (localizador de servicios) https: / /github.com/symfony/cache-contracts/blob/master/CacheInterface.php

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

Vemos, la caché de Symfony tiene una interfaz con solo un método get, falta un método set y una firma diferente para un método get, ya que Symfony usa el método get también como setter cuando proporciona el segundo parámetro invocable.

Como el núcleo de Yii usa internamente esta caché / interfaz de Yii, es difícil (extender Yii / YiiBase) si no imposible en algunos lugares, reescribir las llamadas a esa interfaz.

Además, el caché de Symfony no es de nuestra clase, por lo que no podemos reescribir su interfaz para que encaje con la interfaz de caché de Yii.

Entonces aquí viene el patrón del adaptador para rescatar. Escribiremos un mapeo = un adaptador intermedio que mapeará las llamadas de la interfaz de caché de Yii a la interfaz de caché de Symfony

Se vería así

    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

Este es un ejemplo de implementación de adaptador:

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);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.