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.
oop
design-patterns
adapter
software-design
AksharRoop
fuente
fuente
Respuestas:
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).
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.fuente
Cómo convertir a un francés en una persona normal ...
Ejemplo
fuente
Convierta una interfaz en otra interfaz.
Para conectar la energía, tenemos diferentes interfaces en todo el mundo. Usando el Adaptador, podemos conectarnos fácilmente de la misma manera.
fuente
Aquí hay un ejemplo que simula la conversión
analog data
adigit 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.
fuente
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
fuente
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
fuente
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.
Puede ver el ejemplo completo de Qt-Dbus aquí -
http://www.tune2wizard.com/linux-qt-signals-and-slots-qt-d-bus/
fuente
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.
fuente
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.
fuente
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.
Los resultados serán:
fuente
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.
fuente
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.
fuente
Según el libro "Patrones de diseño C # 3.0" de Judith Bishop, Apple utilizó el patrón Adaptador para adaptar Mac OS para que funcione con productos Intel (explicado en el Capítulo 4, extracto aquí 2 )
fuente
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: -
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
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í
fuente
Este es un ejemplo de implementación de adaptador:
fuente