En ingeniería de software , el patrón adaptador es un patrón de diseño de software (también conocido como envoltorio , una denominación alternativa compartida con el patrón decorador ) que permite que la interfaz de una clase existente se utilice como otra interfaz. [ 1 ] : 244 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 objetos de documento (DOM) de un documento XML en una estructura de árbol que se puede visualizar.
Descripción general
El patrón de diseño adaptador [ 2 ] es uno de los veintitrés patrones de diseño conocidos del Gang of Four 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 el cliente requiera?
- ¿Cómo pueden funcionar juntas las 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 este tipo de problemas:
- Defina una
adapterclase separada que convierta la interfaz (incompatible) de una clase (adaptee) en otra interfaz (target) que los clientes requieren. - Trabaje a través de un
adapterpara trabajar con (reutilizar) clases que no tienen la interfaz requerida.
La idea clave de este patrón es trabajar a través de un elemento separado adapterque adapta la interfaz de una clase (ya existente) sin modificarla.
Los clientes no saben si trabajan targetdirectamente con una clase o a través adapterde una clase que no tiene la targetinterfaz.
Véase también el diagrama de clases UML a continuación.
Definición
Un adaptador permite que dos interfaces incompatibles funcionen juntas. Esta es la definición práctica de un adaptador. Las interfaces pueden ser incompatibles, pero la funcionalidad interna debe ser la adecuada. El patrón de diseño de 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 usar un adaptador cuando el contenedor debe respetar una interfaz particular y debe admitir un comportamiento polimórfico . Alternativamente, un patrón decorador permite agregar o modificar el comportamiento de una interfaz en tiempo de ejecución, y un patrón fachada se usa cuando se desea una interfaz más fácil o simple para un objeto subyacente. [ 1 ] : 243, 252, 258, 260
Estructura
Diagrama de clases UML

En el diagrama de clases UML anterior , la clase que requiere una interfaz no puede reutilizar la clase 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 en que se implementa latargetinterfaz es 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 adaptador de objetos
En este patrón de adaptador, el adaptador contiene una instancia de la clase que envuelve. En esta situación, el adaptador realiza llamadas a la instancia del objeto envuelto .


Patrón 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 típico que la interfaz esperada se cree como una clase de interfaz pura , especialmente en lenguajes como Java (antes de JDK 1.8) que no admiten la herencia múltiple de clases. [ 1 ] : 244


Otra forma de patrón adaptador de tiempo de ejecución
Motivación a partir de la solución en tiempo de compilación
Se desea proporcionar algunos datos, supongamos que tenemos algunos datos. Una solución en tiempo de compilación es:classAclassBString
clase B. setStringData ( claseA . getStringData ());Sin embargo, supongamos que el formato de los datos de cadena debe variar. Una solución en tiempo de compilación es usar la herencia:
public class Format1ClassA extends ClassA { @Override public String getStringData () { return format ( toString ()); } }y tal vez crear el objeto con el "formato" correcto en tiempo de ejecución mediante el 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" intermedia y escriba una implementación de esa interfaz de proveedor que encapsule la fuente de datos,
ClassAen este ejemplo, y genere los datos formateados según corresponda:interfaz StringProvider { public String getStringData (); }clase pública ClassAFormat1 implementa StringProvider { claseA privada claseA = nulo ;public ClassAFormat1 ( ClassA final a ) { classA = a ; }public String getStringData () { return format ( classA . getStringData ()); }private String format ( final String sourceValue ) { // Manipula la cadena de origen para darle el 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:
public class ClassAFormat1Adapter extends Adapter { public Object adapt ( final Object anObject ) { return new ClassAFormat1 (( ClassA ) anObject ); } }
- Regístrelo
adapteren un registro global, para queadapterpueda consultarse en tiempo de ejecución:AdapterFactory.getInstance ( ) . registerAdapter ( ClassA.class , ClassAFormat1Adapter.class , " format1 " ) ;
- En el código, cuando se desee transferir datos de
ClassAaClassB, escriba:Adapter adapter = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , StringProvider . class , "format1" ); StringProvider provider = ( StringProvider ) adapter . adapt ( classA ); String string = provider . getStringData (); classB . setStringData ( string );
o, de forma más concisa:
claseB . setStringData ((( StringProvider ) AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , StringProvider . class , "format1" ) . adapt ( classA )) . getStringData () );
- La ventaja se puede apreciar en que, si se desea transferir los datos en un segundo formato, basta con buscar el adaptador/proveedor correspondiente:
Adapter adapter = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , StringProvider . class , "format2" );
- Y si se desea generar los datos
ClassAcomo, por ejemplo, datos de imagen en :ClassCAdapter adapter = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , ImageProvider . class , "format2" ); ImageProvider provider = ( ImageProvider ) adapter . adapt ( classA ); classC . setImage ( provider . getImage ());
- De esta forma, el uso de adaptadores y proveedores permite múltiples "vistas" desde
ClassByClassChaciaClassAsin necesidad de modificar la jerarquía de clases. En general, proporciona un mecanismo para flujos de datos arbitrarios entre objetos que pueden adaptarse 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 la clase adaptada como parámetro. Este parámetro se pasará a un miembro de instancia de . Cuando se llama al método cliente, tendrá acceso a la instancia adaptada que permite acceder a los datos necesarios de la adaptada y realizar operaciones sobre esos datos para generar el resultado deseado.[ClassName]To[Interface]AdapterDAOToProviderAdapter[ClassName]To[Interface]Adapter
Java
paquete org.wikipedia.examples ;interfaz ILightningPhone { void recargar (); void usarLightning (); }interface IMicroUsbPhone { void recharge (); void useMicroUsb (); }clase Iphone implementa ILightningPhone { private boolean connector ;@Override public void useLightning () { connector = true ; System . out . println ( "Lightning conectado" ); }@Override public void recharge () { if ( connector ) { System . out . println ( "Recarga iniciada" ); System . out . println ( "Recarga finalizada" ); } else { System . out . println ( "Conecta primero el Lightning" ); } } }clase Android implementa IMicroUsbPhone { private boolean connector ;@Override public void useMicroUsb () { connector = true ; System . out . println ( "MicroUsb conectado" ); }@Override public void recharge () { if ( connector ) { System . out . println ( "Recarga iniciada" ); System . out . println ( "Recarga finalizada" ); } else { System . out . println ( "Conectar MicroUsb primero" ); } } } /* exponiendo la interfaz de destino mientras se envuelve el objeto de origen */ class LightningToMicroUsbAdapter implements IMicroUsbPhone { private final ILightningPhone lightningPhone ;public LightningToMicroUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; }@Override public void useMicroUsb () { System . out . println ( "MicroUsb conectado" ); lightningPhone . useLightning (); }@Override public void recharge () { lightningPhone . recharge (); } }public class AdapterDemo { static void rechargeMicroUsbPhone ( IMicroUsbPhone phone ) { phone . useMicroUsb (); phone . recharge (); }static void rechargeLightningPhone ( ILightningPhone phone ) { phone . useLightning (); phone . recharge (); }public static void main ( String [] args ) { Android android = new Android (); Iphone iPhone = new Iphone ();System.out.println ( " Recargando Android con MicroUsb" ) ; rechargeMicroUsbPhone ( android ) ;System.out.println ( " Recargando iPhone con Lightning" ) ; rechargeLightningPhone ( iPhone ) ;System.out.println ( " Recargando iPhone con MicroUsb" ) ; rechargeMicroUsbPhone ( new LightningToMicroUsbAdapter ( iPhone ) ); } }Producción
Recarga de Android con MicroUSB Conexión MicroUSB Se ha iniciado la recarga. Recarga finalizada Recargar el iPhone con Lightning Conexión Lightning Se ha iniciado la recarga. Recarga finalizada Recargar iPhone con MicroUSB Conexión MicroUSB Conexión Lightning Se ha iniciado la recarga. Recarga finalizada
Pitón
""" Ejemplo de patrón adaptador. """ from abc import ABCMeta , abstractmethod from typing import NoReturnRECARGA : lista [ str ] = [ "Recarga iniciada." , "Recarga finalizada." ] ADAPTADORES_DE_ALIMENTACIÓN : diccionario [ str , str ] = { "Android" : "MicroUSB" , "iPhone" : "Lightning" } MENSAJE_CONECTADO : str = " {} conectado." MENSAJE_CONEXIÓN_PRIMERA : str = "Conectar {} primero."clase RechargeTemplate ( metaclass = ABCMeta ): @abstractmethod def recharge ( self ) -> NoReturn : raise NotImplementedError ( "Deberías implementar esto." )clase FormatIPhone ( RechargeTemplate ): @abstractmethod def use_lightning ( self ) -> NoReturn : raise NotImplementedError ( "Deberías implementar esto." )clase FormatAndroid ( RechargeTemplate ): @abstractmethod def use_micro_usb ( self ) -> NoReturn : raise NotImplementedError ( "Deberías implementar esto." )clase IPhone ( FormatIPhone ): __name__ : str = "iPhone"def __init__ ( self ): self . connector : bool = Falsedef use_lightning ( self ) -> None : self . connector = True print ( CONNECTED_MSG . format ( POWER_ADAPTERS [ self . __name__ ]))def recargar ( self ) -> Ninguno : si self . conector : para estado en RECARGA : imprimir ( estado ) de lo contrario : imprimir ( CONNECT_FIRST_MSG . formato ( POWER_ADAPTERS [ self . __name__ ]))clase Android ( FormatAndroid ): __name__ : str = "Android"def __init__ ( self ) -> None : self . connector : bool = Falsedef use_micro_usb ( self ) -> None : self . connector = True print ( CONNECTED_MSG . format ( POWER_ADAPTERS [ self . __name__ ]))def recargar ( self ) -> Ninguno : si self . conector : para estado en RECARGA : imprimir ( estado ) de lo contrario : imprimir ( CONNECT_FIRST_MSG . formato ( POWER_ADAPTERS [ self . __name__ ]))clase IPhoneAdapter ( FormatAndroid ): def __init__ ( self , mobile : FormatAndroid ) -> None : self . mobile : FormatAndroid = mobiledef recargar ( self ) -> Ninguno : self . móvil . recargar ()def use_micro_usb ( self ) -> None : print ( CONNECTED_MSG . format ( POWER_ADAPTERS [ "Android" ])) self . mobile . use_lightning ()clase AndroidRecharger : def __init __ ( self ) - > None : self.phone : Android = Android ( ) self.phone.use_micro_usb ( ) self.phone.recharge ( )clase IPhoneMicroUSBRecharger : def __init __ ( self ) - > None : self.phone : IPhone = IPhone ( ) self.phone_adapter : IPhoneAdapter = IPhoneAdapter ( self.phone ) self.phone_adapter.use_micro_usb ( ) self.phone_adapter.recharge ( )clase IPhoneRecharger : def __init __ ( self ) - > None : self.phone : IPhone = IPhone ( ) self.phone.use_lightning ( ) self.phone.recharge ( )print ( "Recargando Android con cargador MicroUSB." ) AndroidRecharger () print ()print ( "Recargando iPhone con MicroUSB usando el patrón del adaptador." ) IPhoneMicroUSBRecharger () print ()print ( "Recargando iPhone con el cargador de iPhone." ) IPhoneRecharger ()DO#
espacio de nombres Wikipedia.Ejemplos ;usando el sistema ;interface ILightningPhone { void ConnectLightning (); void Recharge (); }interface IUsbPhone { void ConnectUsb (); void Recharge (); }clase sellada AndroidPhone : IUsbPhone { bool privado isConnected ;public void ConnectUsb ( ) { this.isConnected = true ; Console.WriteLine ( " Teléfono Android conectado." ) ; }public void Recharge ( ) { if ( this.isConnected ) { Console.WriteLine ( " Teléfono Android recargando." ) ; } else { Console.WriteLine ( " Conecta primero el cable USB." ) ; } } }clase sellada ApplePhone : ILightningPhone { bool privado isConnected ;public void ConnectLightning () { this.isConnected = true ; Console.WriteLine ( " Teléfono Apple conectado ." ) ; }public void Recharge () { if ( this.isConnected ) { Console.WriteLine ( " Recargando teléfono Apple." ); } else { Console.WriteLine ( " Conecta primero el cable Lightning." ) ; } } }clase sellada LightningToUsbAdapter : IUsbPhone { privado readonly ILightningPhone lightningPhone ;bool privado isConnected ;public LightningToUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; }public void ConnectUsb () { this . lightningPhone . ConnectLightning (); }public void Recargar ( ) { this.lightningPhone.Recargar ( ) ; } }public class AdapterDemo { static void Main ( string [] args ) { ILightningPhone applePhone = new ApplePhone (); IUsbPhone adapterCable = new LightningToUsbAdapter ( applePhone ); adapterCable . ConnectUsb (); adapterCable . Recharge (); } }Producción:
Teléfono Apple conectado. Teléfono Apple recargándose.Véase también
- Patrones de diseño Java para adaptadores - Adaptador
- La delegación es muy relevante para el patrón adaptador de objetos.
- Principio de inversión de dependencias , que puede entenderse como la aplicación del patrón adaptador, en el que la clase de alto nivel define su propia interfaz (adaptador) al módulo de bajo nivel (implementado por una clase adaptada).
- Arquitectura de puertos y adaptadores
- Calce
- Función de envoltura
- Biblioteca de envoltura
Referencias
- 1 2 3 Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy ; Bates, Bert (2004). Head First Design Patterns . O'Reilly Media . pág. 244. ISBN 978-0-596-00712-6. OCLC 809772256 . Archivado del original (edición 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 . págs. 139 y ss . ISBN 0-201-63361-2.
- ↑ "El patrón de diseño Adaptador: problema, solución y aplicabilidad" . w3sDesign.com . Archivado del original el 28 de agosto de 2017. Consultado el 12 de agosto de 2017 .
- ↑ "El patrón de diseño Adaptador: Estructura y Colaboración" . w3sDesign.com . Archivado del original el 28 de agosto de 2017. Consultado el 12 de agosto de 2017 .
- patrones de diseño de software