En ingeniería de software , el patrón adaptador es un patrón de diseño de software (también conocido como wrapper , un nombre alternativo compartido con el patrón decorador ) que permite que la interfaz de una clase existente se use como otra interfaz. [1] A menudo se utiliza para hacer que las clases existentes funcionen con otras sin modificar su código fuente .
Un ejemplo es un adaptador que convierte la interfaz de un modelo de objeto de documento de un documento XML en una estructura de árbol que se puede mostrar.
Descripción general
El patrón de diseño adaptador [2] es uno de los veintitrés patrones de diseño conocidos del Grupo de los Cuatro que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.
El patrón de diseño del adaptador resuelve problemas como: [3]
- ¿Cómo se puede reutilizar una clase que no tiene una interfaz que requiere un cliente?
- ¿Cómo pueden trabajar juntas clases que tienen interfaces incompatibles?
- ¿Cómo se puede proporcionar una interfaz alternativa para una clase?
A menudo, una clase (ya existente) no se puede reutilizar simplemente porque su interfaz no se ajusta a la interfaz que requieren los clientes.
El patrón de diseño del adaptador describe cómo resolver estos problemas:
- Defina una
adapterclase separada que convierta la interfaz (incompatible) de una clase (adaptee) en otra interfaz (target) que requieran los clientes. - Trabajar
adaptercon (reutilizar) clases que no tienen la interfaz requerida.
La idea clave de este patrón es trabajar a través de un separador adapterque adapta la interfaz de una clase (ya existente) sin cambiarla.
Los clientes no saben si trabajan con una targetclase directamente o a través de adapteruna clase que no tiene la targetinterfaz.
Vea también el diagrama de clases UML a continuación.
Definición
Un adaptador permite que dos interfaces incompatibles trabajen juntas. Esta es la definición real de un adaptador. Las interfaces pueden ser incompatibles, pero la funcionalidad interna debe adaptarse a la necesidad. El patrón de diseño del adaptador permite que clases que de otro modo serían incompatibles trabajen juntas al convertir la interfaz de una clase en una interfaz esperada por los clientes.
Uso
Se puede utilizar un adaptador cuando el contenedor debe respetar una interfaz particular y debe admitir un comportamiento polimórfico . Alternativamente, un decorador permite agregar o alterar el comportamiento de una interfaz en tiempo de ejecución, y se utiliza una fachada cuando se desea una interfaz más sencilla o más fácil para un objeto subyacente. [4]
Estructura
Diagrama de clases UML

En el diagrama de clases UML anterior , la clase que requiere una interfaz no puede reutilizarla directamente porque su interfaz no se ajusta a la interfaz. En cambio, funciona a través de una clase que implementa la interfaz en términos de :
clienttargetadapteetargetclientadaptertargetadaptee
- La
object adapterforma implementa latargetinterfaz delegando a unadapteeobjeto en tiempo de ejecución (adaptee.specificOperation()). - La
class adapterforma implementa latargetinterfaz heredando de unaadapteeclase en tiempo de compilación (specificOperation()).
Patrón de adaptador de objetos
En este patrón de adaptador, el adaptador contiene una instancia de la clase que encapsula. En esta situación, el adaptador realiza llamadas a la instancia del objeto encapsulado .

_pattern_in_LePUS3.png/500px-Adapter(Object)_pattern_in_LePUS3.png)
Patrón de adaptador de clase
Este patrón de adaptador utiliza múltiples interfaces polimórficas que implementan o heredan tanto la interfaz esperada como la interfaz preexistente. Es habitual que la interfaz esperada se cree como una clase de interfaz pura, especialmente en lenguajes como Java (anterior a JDK 1.8) que no admiten la herencia múltiple de clases. [1]

_pattern_in_LePUS3.png/500px-Adapter(Class)_pattern_in_LePUS3.png)
Otra forma de patrón de adaptador de tiempo de ejecución
Motivación a partir de la solución en tiempo de compilación
Se desea classAproporcionar classBalgunos datos, supongamos que algunos Stringdatos. Una solución en tiempo de compilación es:
clase B. setStringData ( claseA . getStringData ());
Sin embargo, supongamos que se debe modificar el formato de los datos de la cadena. Una solución en tiempo de compilación es utilizar la herencia:
clase pública Format1ClassA extiende ClassA { @Override public String getStringData () { devolver formato ( toString ()); } }
y quizás crear el objeto "formateado" correctamente en tiempo de ejecución por medio del patrón de fábrica .
Solución de adaptador en tiempo de ejecución
Una solución que utiliza "adaptadores" procede de la siguiente manera:
- Defina una interfaz de "proveedor" intermediario y escriba una implementación de esa interfaz de proveedor que envuelva la fuente de los datos,
ClassAen este ejemplo, y genere los datos formateados según corresponda:Interfaz pública StringProvider { String público getStringData (); } clase pública ClassAFormat1 implementa StringProvider { claseA privada claseA = nulo ; public ClassAFormat1 ( ClassA final a ) { classA = a ; } public String getStringData ( ) { formato de retorno ( classA.getStringData ( )); } formato de cadena privada ( string final sourceValue ) { // Manipular la cadena de origen en un formato requerido // por el objeto que necesita los datos del objeto de origen return sourceValue . trim (); } }
- Escriba una clase adaptadora que devuelva la implementación específica del proveedor:
clase pública ClassAFormat1Adapter extiende Adapter { objeto público adapt ( objeto final anObject ) { devolver nuevo ClassAFormat1 (( ClassA ) anObject ); } }
- Registre el
adaptercon un registro global, para queadapterpueda buscarse en tiempo de ejecución:AdapterFactory . getInstance (). registerAdapter ( ClassA . class , ClassAFormat1Adapter . class , "formato1" );
- En el código, cuando se desea transferir datos de
ClassAaClassB, escribir:Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , StringProvider . class , "format1" ); StringProvider proveedor = ( StringProvider ) adaptador . adapt ( classA ); Cadena cadena = proveedor . getStringData (); classB . setStringData ( cadena );
o más concisamente:
claseB .setStringData ( (( StringProvider ) AdapterFactory .getInstance ( ) .getAdapterFromTo ( ClaseA .class , StringProvider .class , " format1 " ) .adaptar ( claseA ) ) .getStringData ( ) ) ;
- La ventaja es que si se desea transferir los datos en un segundo formato, entonces hay que buscar el adaptador/proveedor diferente:
Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , StringProvider . class , "format2" );
- Y si se desea generar los datos
ClassAcomo, por ejemplo, datos de imagen en :Class CAdaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , ImageProvider . class , "format2" ); ImageProvider proveedor = ( ImageProvider ) adaptador . adapt ( classA ); classC . setImage ( proveedor . getImage ());
- De esta manera, el uso de adaptadores y proveedores permite múltiples "vistas" de
ClassByClassChaciaClassAsin tener que alterar la jerarquía de clases. En general, permite un mecanismo para flujos de datos arbitrarios entre objetos que se pueden adaptar a una jerarquía de objetos existente.
Implementación del patrón adaptador
Al implementar el patrón adaptador, para mayor claridad, se puede aplicar el nombre de la clase a la implementación del proveedor; por ejemplo, . Debe tener un método constructor con una variable de clase adaptada como parámetro. Este parámetro se pasará a un miembro de instancia de . Cuando se llama a clientMethod, tendrá acceso a la instancia adaptada que permite acceder a los datos requeridos del adaptado y realizar operaciones sobre esos datos que generan el resultado deseado.
[ClassName]To[Interface]AdapterDAOToProviderAdapter[ClassName]To[Interface]Adapter
Java
interfaz ILightningPhone { void recarga (); void useLightning (); }
interfaz IMicroUsbPhone { void recarga (); void useMicroUsb (); }
la clase Iphone implementa ILightningPhone { conector booleano privado ;
@Override
public void useLightning () { conector = true ; System.out.println ( " Lightning conectado " ) ; }
@Override
public void recharge () { if ( conector ) { System . println ( " Recarga iniciada " ) ; System . println ( " Recarga finalizada" ); } else { System . println ( " Conectar Lightning primero" ); } } }
clase Android implementa IMicroUsbPhone { conector booleano privado ;
@Override
public void useMicroUsb ( ) { conector = true ; System.out.println ( " MicroUsb conectado " ) ; }
@Override
public void recharge () { if ( connector ) { System . println ( " Recarga iniciada" ) ; System . println ( "Recarga finalizada" ); } else { System . println ( "Conectar MicroUsb primero" ); } } } / * exponiendo la interfaz de destino mientras se envuelve el objeto de origen */ class LightningToMicroUsbAdapter implements IMicroUsbPhone { private final ILightningPhone lightningPhone ;
público LightningToMicroUsbAdapter ( ILightningPhone lightningPhone ) { este .lightningPhone = lightningPhone ; }
@Override
public void useMicroUsb ( ) { System.out.println ( " MicroUsb conectado " ) ; lightningPhone.useLightning () ; }
@Override
public void recarga () { lightningPhone.recarga ( ) ; } }
clase pública AdapterDemo { static void rechargeMicroUsbPhone ( IMicroUsbPhone teléfono ) { teléfono.useMicroUsb ( ) ; teléfono.recharge ( ) ; }
static void recargaLightningPhone ( ILightningPhone teléfono ) { teléfono.useLightning ( ) ; teléfono.recarga ( ) ; }
público estático void principal ( String [] args ) { Android android = nuevo Android (); Iphone iPhone = nuevo Iphone ();
Sistema . out . println ( "Recargando android con MicroUsb" );
rechargeMicroUsbPhone ( android );
Sistema . out . println ( "Recargando iPhone con Lightning" );
rechargeLightningPhone ( iPhone );
Sistema . out . println ( "Recargar iPhone con MicroUsb" );
rechargeMicroUsbPhone ( new LightningToMicroUsbAdapter ( iPhone )); } }
Producción
Recargar android con MicroUsb MicroUSB conectado Recarga comenzada Recarga terminada Cómo recargar el iPhone con Lightning Rayo conectado Recarga comenzada Recarga terminada Recargar iPhone con MicroUSB MicroUSB conectado Rayo conectado Recarga comenzada Recarga terminada
Pitón
"""
Ejemplo de patrón adaptador.
"""
from abc import ABCMeta , abstractmethod
NOT_IMPLEMENTED = "Deberías implementar esto."
RECARGA = [ "Recarga iniciada." , "Recarga finalizada." ]
ADAPTADORES DE ALIMENTACIÓN = { "Android" : "MicroUSB" , "iPhone" : "Lightning" }
CONECTADO = " {} conectado."
CONNECT_FIRST = "Conectar {} primero."
clase RechargeTemplate ( metaclase = ABCMeta ):
@abstractmethod
def recarga ( self ):
genera NotImplementedError ( NO_IMPLEMENTADO )
clase FormatIPhone ( RechargeTemplate ):
@abstractmethod
def use_lightning ( self ):
generar NotImplementedError ( NO_IMPLEMENTADO )
clase FormatAndroid ( RechargeTemplate ):
@abstractmethod
def use_micro_usb ( self ):
generar NotImplementedError ( NO_IMPLEMENTADO )
clase IPhone ( FormatIPhone ):
__name__ = "iPhone"
def __init __ ( self )
: self.conector = Falso
def use_lightning ( self ) :
self.conector = True print ( CONECTADO.formato ( ADAPTADORES_DE_ALIMENTACIÓN [ self .__ nombre__ ] ) )
def recarga ( self ) :
if self.conector : for estado in RECARGA : print ( estado ) else : print ( CONECTAR_PRIMERO.formato ( ADAPTADORES_DE_ALIMENTACIÓN [ self .__ nombre__ ] ) )
clase Android ( FormatAndroid ):
__name__ = "Android"
def __init __ ( self )
: self.conector = Falso
def use_micro_usb ( self ) :
self.conector = True print ( CONECTADO.formato ( ADAPTADORES_DE_ALIMENTACIÓN [ self .__ nombre__ ] ) )
def recarga ( self ) :
if self.conector : for estado in RECARGA : print ( estado ) else : print ( CONECTAR_PRIMERO.formato ( ADAPTADORES_DE_ALIMENTACIÓN [ self .__ nombre__ ] ) )
clase IPhoneAdapter ( FormatAndroid ):
def __init __ ( self , mobile )
: self.mobile = mobile
def recarga ( self ) :
self.mobile.recharge ( )
def use_micro_usb ( self ) :
print ( CONECTADO.format ( ADAPTADORES_DE_ALIMENTACIÓN [ " Android " ] ) ) self.mobile.use_lightning ( )
clase AndroidRecharger :
def __init __ ( self )
: self.phone = Android ( ) self.phone.use_micro_usb ( ) self.phone.recharge ( )
clase IPhoneMicroUSBRecharger :
def __init__ ( self ):
self . phone = IPhone ()
self . phone_adapter = IPhoneAdapter ( self . phone )
self . phone_adapter . use_micro_usb ()
self . phone_adapter . recargar ()
clase IPhoneRecharger :
def __init__ ( self ):
self . phone = IPhone ()
self . phone . use_lightning ()
self . phone . recarga ()
print ( "Recargar Android con el cargador MicroUSB." )
AndroidRecharger ()
print ()
print ( "Recargar iPhone con MicroUSB usando patrón de adaptador." )
IPhoneMicroUSBRecharger ()
print ()
imprimir ( "Recargar el iPhone con el cargador de iPhone." )
IPhoneRecharger ()
DO#
interfaz pública ILightningPhone { void ConnectLightning (); void Recharge (); }
Interfaz pública IUsbPhone { void ConnectUsb (); void Recarga (); }
clase pública sellada AndroidPhone : IUsbPhone { bool privado isConnected ; void público ConnectUsb ( ) { this.isConnected = true ; Console.WriteLine ( " Teléfono Android conectado. " ) ; }
public void Recharge () { if(this.isConnected ) { Console.WriteLine ( " Recargando el teléfono Android . " ); } else { Console.WriteLine ( " Primero conecte el cable USB. " ) ; } } }
clase pública sellada ApplePhone : ILightningPhone { bool privado isConnected ; void público ConnectLightning ( ) { this.isConnected = true ; Console.WriteLine ( " Teléfono Apple conectado. " ) ; }
public void Recharge () { if(this.isConnected ) { Console.WriteLine ( " Recarga del teléfono Apple . " ) ; } else { Console.WriteLine ( " Primero conecte el cable Lightning." ) ; } } }
clase pública sellada LightningToUsbAdapter : IUsbPhone { privada de solo lectura ILightningPhone lightningPhone ; bool privado isConnected ; público LightningToUsbAdapter ( ILightningPhone lightningPhone ) { this.lightningPhone = lightningPhone ; } público void ConnectUsb ( ) { this.lightningPhone.ConnectLightning ( ) ; }
public void Recarga ( ) { this.lightningPhone.Recarga ( ) ; } }
public void Main () { ILightningPhone applePhone = new ApplePhone (); IUsbPhone cable_adaptador = new LightningToUsbAdapter ( applePhone ) ; cable_adaptador.ConnectUsb ( ) ; cable_adaptador.Recharge ( ); }
Producción:
Teléfono Apple conectado.
Cable adaptador conectado.
Teléfono Apple recargándose.
Véase también
- Patrones de diseño de Java para adaptadores - Adaptador
- Delegación , muy relevante para el patrón de adaptador de objetos.
- Principio de inversión de dependencia , que puede considerarse como la aplicación del patrón adaptador, cuando la clase de alto nivel define su propia interfaz (adaptador) con el módulo de bajo nivel (implementado por una clase adaptada).
- Arquitectura de puertos y adaptadores
- Calce
- Función de envoltura
- Biblioteca de envoltorios
Referencias
- ^ ab Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy ; Bates, Bert (2004). Patrones de diseño Head First. O'Reilly Media . pág. 244. ISBN 978-0-596-00712-6. OCLC 809772256. Archivado desde el original (libro de bolsillo) el 4 de mayo de 2013. Consultado el 30 de abril de 2013 .
- ^ Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley . pp. 139ff. ISBN. 0-201-63361-2.
- ^ "El patrón de diseño Adapter - Problema, solución y aplicabilidad". w3sDesign.com . Archivado desde el original el 28 de agosto de 2017. Consultado el 12 de agosto de 2017 .
- ^ Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (eds.). Patrones de diseño Head First (libro de bolsillo) . Vol. 1. O'Reilly Media . Págs. 243, 252, 258, 260. ISBN. 978-0-596-00712-6. Recuperado el 2 de julio de 2012 .
- ^ "El patrón de diseño Adapter - Estructura y colaboración". w3sDesign.com . Archivado desde el original el 28 de agosto de 2017. Consultado el 12 de agosto de 2017 .