strategy pattern oop design-patterns adapter software-design

oop - pattern - adaptador: cualquier ejemplo real de patrón de adaptador



proxy pattern (12)

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

¿Puede compartir algún estudio de caso del patrón de adaptador?

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


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

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

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


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

Proporciona un adaptador que convierte datos de dígitos float en datos binarios, probablemente no sea útil en el mundo real, simplemente 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.


Cómo convertir a una persona francesa 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));


Convierta una interfaz en otra interfaz.

Cualquier ejemplo real de patrón de adaptador

Para conectar la energía, tenemos diferentes interfaces en todo el mundo. Usando Adapter podemos conectarnos fácilmente como sabios.


El ejemplo de @Justice o no habla sobre el patrón del adaptador claramente. Ampliando su respuesta - Tenemos una interfaz existente IDataStore que nuestro código de consumidor usa y no podemos cambiarlo. Ahora se nos pide que usemos una nueva clase genial de la biblioteca XYZ que hace lo que queremos implementar, pero pero, pero no podemos cambiar esa clase para extender nuestro IDataStore, ¿ya hemos visto el problema? Creando una nueva clase - ADAPTADOR, que implementa la interfaz que nuestro código de consumidor espera, 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 deseamos.


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


Muchos ejemplos de Adapter son triviales o poco realistas ( Rectángulo vs. LegacyRectangle, Ratchet vs. Socket , SquarePeg vs RoundPeg , Duck vs. Turkey ). Peor aún, muchos no muestran adaptadores múltiples para diferentes Adaptados ( alguien citó Arrays.asList de Java como un ejemplo del patrón del adaptador ). Adaptar una interfaz de una sola clase para que funcione con otra parece ser 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 implementaciones múltiples de adaptadores para diferentes adaptadas .

El mejor ejemplo que encontré está en el Capítulo 26 de Aplicación de UML y patrones: una introducción al análisis y diseño orientado a objetos y desarrollo iterativo (3ª edición) . Las siguientes imágenes son del material del instructor provisto 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 la codificación estricta de 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. Esos son todos los módulos externos que pueden variar, y para los cuales no podemos modificar el 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).

No vemos en la figura anterior los adaptadores 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 a través de SOAP en un sistema SAP.


Puede encontrar una implementación de PHP del patrón de 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 Adapter es que se presenta en dos formas: un adaptador de clase basado en herencia múltiple y un adaptador de objeto que depende de la composición. El ejemplo anterior se basa en la composición.


Puede usar el patrón de diseño del Adaptador cuando tenga que tratar con diferentes interfaces con un comportamiento similar (lo que generalmente significa clases con un comportamiento similar pero con diferentes métodos). Un ejemplo de esto sería una clase para conectarse a un televisor Samsung y otra para conectarse 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étodo y firmas). Estas diferentes implementaciones específicas del proveedor se denominan Adaptado en los diagramas UML.

Entonces, en su código (llamado Cliente en los diagramas UML), en lugar de código fijo , las llamadas de método de cada proveedor (o Adaptado ), podría crear una interfaz genérica (llamada Objetivo en diagramas UML) para ajustar estos comportamientos y trabajos similares. con solo un tipo de objeto

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

Para que se den 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, por lo que puede profundizar para ver cómo serí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 Adaptados falsos en el paquete com.thirdparty.libs . ¡Espero eso ayude!

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


Un ejemplo real es Qt-Dbus.

El qt-dbus tiene una utilidad para generar el adaptador y el código de interfaz del archivo xml proporcionado. Aquí están 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.

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

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


Un ejemplo real puede ser informar documentos en una aplicación. Código simple como aquí.

Los adaptadores, creo, 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


Use Adapter cuando tenga una interfaz que no puede cambiar, pero que debe usar. Véalo como si fuera el nuevo tipo en una oficina y no puede hacer que los canosos sigan sus reglas; debe adaptarse a los suyos. 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 necesita ser utilizada con registros de producción que son demasiado grandes para leer en la memoria pero el usuario aún necesita navegar a través de ella! Una solución sería implementar un caché que almacene la primera página, la siguiente, la anterior y la última página. Lo que queremos es que 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 memoria caché. En el fondo tenemos una cadena de archivos haciendo toda la magia. Al hacerlo, solo tenemos cuatro páginas en memoria en lugar de todo el archivo.

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

Y quién sabe, mañana el jefe quiere comenzar a leer los archivos de una tabla de base de datos. Todo lo que hace es extender IDataStore a SQLDataStore como lo hizo para Cache, configurar la conexión en segundo plano. Cuando hacen clic en Página siguiente, genera la consulta SQL necesaria para buscar las próximas doscientas filas de la base de datos.

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