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.
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]
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:
adapter
clase separada que convierta la interfaz (incompatible) de una clase ( adaptee
) en otra interfaz ( target
) que requieran los clientes.adapter
con (reutilizar) clases que no tienen la interfaz requerida.La idea clave de este patrón es trabajar a través de un separador adapter
que adapta la interfaz de una clase (ya existente) sin cambiarla.
Los clientes no saben si trabajan con una target
clase directamente o a través de adapter
una clase que no tiene la target
interfaz.
Vea también el diagrama de clases UML a continuació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.
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]
Patrón | Intención |
---|---|
Adaptador o envoltorio | Convierte una interfaz en otra para que coincida con lo que espera el cliente. |
Decorador | Agrega dinámicamente responsabilidad a la interfaz al envolver el código original |
Delegación | Apoyar la “composición por encima de la herencia” |
Fachada | Proporciona una interfaz simplificada |
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 :client
target
adaptee
target
client
adapter
target
adaptee
object adapter
forma implementa la target
interfaz delegando a un adaptee
objeto en tiempo de ejecución ( adaptee.specificOperation()
).class adapter
forma implementa la target
interfaz heredando de una adaptee
clase en tiempo de compilación ( specificOperation()
).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 .
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]
Se desea classA
proporcionar classB
algunos datos, supongamos que algunos String
datos. 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 .
Una solución que utiliza "adaptadores" procede de la siguiente manera:
ClassA
en 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 (); } }
clase pública ClassAFormat1Adapter extiende Adapter { objeto público adapt ( objeto final anObject ) { devolver nuevo ClassAFormat1 (( ClassA ) anObject ); } }
adapter
con un registro global, para que adapter
pueda buscarse en tiempo de ejecución:AdapterFactory . getInstance (). registerAdapter ( ClassA . class , ClassAFormat1Adapter . class , "formato1" );
ClassA
a ClassB
, 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 ( ) ) ;
Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , StringProvider . class , "format2" );
ClassA
como, por ejemplo, datos de imagen en :Class C
Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . class , ImageProvider . class , "format2" ); ImageProvider proveedor = ( ImageProvider ) adaptador . adapt ( classA ); classC . setImage ( proveedor . getImage ());
ClassB
y ClassC
hacia ClassA
sin 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.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]Adapter
DAOToProviderAdapter
[ClassName]To[Interface]Adapter
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 ( "Recargando iPhone con MicroUsb" ); rechargeMicroUsbPhone ( new LightningToMicroUsbAdapter ( iPhone )); } }
Producción
Recargar android con MicroUsbMicroUSB conectadoRecarga comenzadaRecarga terminadaCómo recargar el iPhone con LightningRayo conectadoRecarga comenzadaRecarga terminadaRecargar iPhone con MicroUSBMicroUSB conectadoRayo conectadoRecarga comenzadaRecarga terminada
""" Ejemplo de patrón adaptador. """ from abc import ABCMeta , abstractmethodNOT_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 ()print ( "Recargar el iPhone con el cargador de iPhone." ) IPhoneRecharger ()
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.