Este artículo puede contener ejemplos indiscriminados , excesivos o irrelevantes . ( Enero de 2011 ) |
En ingeniería de software , el patrón de adaptador es un patrón de diseño de software (también conocido como envoltorio , un nombre alternativo compartido con el patrón de decorador ) que permite que la interfaz de una clase existente se utilice 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 objetos de documento de un documento XML en una estructura de árbol que se puede mostrar.
El patrón de diseño del adaptador [2] es uno de los veintitrés patrones de diseño conocidos de 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]
A menudo, una clase (ya existente) no se puede reutilizar solo 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
para trabajar con (reutilizar) clases que no tienen la interfaz requerida.La idea clave en este patrón es trabajar a través de un separado adapter
que adapte la interfaz de una clase (ya existente) sin cambiarla.
Los clientes no saben si trabajan con una target
clase directamente o mediante adapter
una clase que no tiene la target
interfaz.
Consulte también el diagrama de clases de UML a continuación.
Un adaptador permite que dos interfaces incompatibles funcionen 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 las 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 usar un adaptador cuando el contenedor debe respetar una interfaz particular y debe admitir un comportamiento polimórfico . Alternativamente, un decorador hace posible agregar o alterar el comportamiento de una interfaz en tiempo de ejecución, y se usa una fachada cuando se desea una interfaz más fácil o más simple 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 responsabilidad dinámicamente a la interfaz al envolver el código original |
Delegación | Apoyar la "composición sobre la herencia" |
Fachada | Proporciona una interfaz simplificada. |
En el diagrama de clases de UML anterior , la client
clase que requiere una target
interfaz no puede reutilizar la adaptee
clase directamente porque su interfaz no se ajusta a la target
interfaz. En cambio, client
funciona a través de una adapter
clase que implementa la target
interfaz en términos de adaptee
:
object adapter
forma implementa la target
interfaz delegando a un adaptee
objeto en tiempo de ejecución ( adaptee.specificOperation()
).class adapter
forma en que 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 envuelve. En esta situación, el adaptador realiza llamadas a la instancia del objeto empaquetado .
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]
Se desea classA
suministrar classB
algunos datos, supongamos algunos String
datos. Una solución de tiempo de compilación es:
claseB . setStringData ( classA . getStringData ());
Sin embargo, suponga que se debe variar el formato de los datos de la cadena. Una solución de tiempo de compilación es usar la herencia:
La clase pública Format1ClassA extiende ClassA { @Override public String getStringData () { formato de retorno ( toString ()); } }
y quizás crear el objeto de "formateo" correcto en tiempo de ejecución mediante el 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 { public String getStringData (); }La clase pública ClassAFormat1 implementa StringProvider { privada ClassA classA = null ; public ClassAFormat1 ( final ClassA a ) { classA = a ; } public String getStringData () { formato de retorno ( classA . getStringData ()); } private String format ( final String sourceValue ) { // Manipular la cadena fuente en un formato requerido // por el objeto que necesita el retorno de datos del objeto fuente sourceValue . recortar (); } }
La clase pública ClassAFormat1Adapter extiende el adaptador { public Object adapt ( objeto final anObject ) { return new ClassAFormat1 (( ClassA ) anObject ); } }
adapter
con un registro global, de modo que adapter
se pueda buscar en tiempo de ejecución:AdapterFactory . getInstance (). registerAdapter ( ClassA . clase , ClassAFormat1Adapter . clase , "formato1" );
ClassA
a ClassB
, escriba:Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . clase , StringProvider . clase , "formato1" ); Proveedor StringProvider = adaptador ( StringProvider ) . adaptar ( classA ); String string = proveedor . getStringData (); claseB . setStringData ( cadena );
o más concisamente:
claseB . setStringData ( (( StringProvider ) AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . clase , StringProvider . clase , "formato1" ) . adapt ( classA )) . getStringData ());
Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . clase , StringProvider . clase , "formato2" );
ClassA
como, digamos, datos de imagen en :Class C
Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . clase , ImageProvider . clase , "formato2" ); Proveedor de ImageProvider = adaptador ( ImageProvider ) . adaptar ( classA ); classC . setImage ( proveedor . getImage ());
ClassB
y ClassC
en 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 del 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 adaptable como parámetro. Este parámetro se pasará a un miembro de instancia de . Cuando se llama al clientMethod, tendrá acceso a la instancia adaptee que permite acceder a los datos requeridos del adaptee y realizar operaciones en esos datos que generan la salida deseada.[ClassName]To[Interface]Adapter
DAOToProviderAdapter
[ClassName]To[Interface]Adapter
interfaz LightningPhone { void recharge (); void useLightning (); }interfaz MicroUsbPhone { recarga vacía (); void useMicroUsb (); } la clase Iphone implementa LightningPhone { conector booleano privado ; @Override public void useLightning () { conector = verdadero ; Sistema . fuera . println ( "Rayo conectado" ); } @Override public void recharge () { if ( conector ) { System . fuera . println ( "Recarga iniciada" ); Sistema . fuera . println ( "Recarga finalizada" ); } else { System . fuera . println ( "Conectar Lightning primero" ); } } }clase Android implementa MicroUsbPhone { conector booleano privado ; @Override public void useMicroUsb () { conector = verdadero ; Sistema . fuera . println ( "MicroUsb conectado" ); } @Override public void recharge () { if ( conector ) { System . fuera . println ( "Recarga iniciada" ); Sistema . fuera . println ( "Recarga finalizada" ); } else { System . fuera . println ( "Conectar MicroUsb primero" ); } } } / * exponiendo la interfaz de destino mientras envuelve el objeto de origen * / class LightningToMicroUsbAdapter implementa MicroUsbPhone { LightningPhone final privado lightningPhone ; public LightningToMicroUsbAdapter ( LightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; } @Override public void useMicroUsb () { System . fuera . println ( "MicroUsb conectado" ); lightningPhone . useLightning (); } @Override public void recharge () { lightningPhone . recargar (); } }público de clase AdapterDemo { static void rechargeMicroUsbPhone ( MicroUsbPhone teléfono ) { teléfono . useMicroUsb (); teléfono . recargar (); } static void rechargeLightningPhone ( teléfono LightningPhone ) { teléfono . useLightning (); teléfono . recargar (); } public static void main ( String [] args ) { Android android = new Android (); Iphone iPhone = nuevo Iphone (); Sistema . fuera . println ( "Recarga de Android con MicroUsb" ); rechargeMicroUsbPhone ( android ); Sistema . fuera . println ( " Recargar iPhone con Lightning" ); rechargeLightningPhone ( iPhone ); Sistema . fuera . println ( "Recarga de iPhone con MicroUsb" ); rechargeMicroUsbPhone ( nuevo LightningToMicroUsbAdapter ( iPhone )); } }
Producción
Recarga de Android con MicroUsb MicroUsb conectado Recarga iniciada Recarga finalizada Recargar iPhone con Lightning Rayo conectado Recarga iniciada Recarga finalizada Recarga de iPhone con MicroUsb MicroUsb conectado Rayo conectado Recarga iniciada Recarga finalizada
"" " Ejemplo de patrón de adaptador. " "" De abc import ABCMeta , método abstractoNOT_IMPLEMENTED = "Debería implementar esto".RECARGA = [ "Recarga iniciada". , "Recarga finalizada". ]POWER_ADAPTERS = { "Android" : "MicroUSB" , "iPhone" : "Lightning" }CONECTADO = " {} conectado". CONNECT_FIRST = "Conectar {} primero".class RechargeTemplate : __metaclass__ = ABCMeta @abstractmethod def recharge ( self ): levanta NotImplementedError ( NOT_IMPLEMENTED )class FormatIPhone ( RechargeTemplate ): @abstractmethod def use_lightning ( self ): raise NotImplementedError ( NOT_IMPLEMENTED )class FormatAndroid ( RechargeTemplate ): @abstractmethod def use_micro_usb ( self ): raise NotImplementedError ( NOT_IMPLEMENTED )clase IPhone ( FormatIPhone ): __name__ = "iPhone" def __init__ ( self ): self . conector = Falso def use_lightning ( self ): self . conector = Impresión verdadera ( formato CONNECTED . ( POWER_ADAPTERS [ self . __name__ ])) def recharge ( self ): if self . conector : para el estado en RECARGA : imprimir ( estado ) más : imprimir ( CONNECT_FIRST . formato ( POWER_ADAPTERS [ self . __name__ ]))clase Android ( FormatAndroid ): __name__ = "Android" def __init__ ( self ): self . conector = Falso def use_micro_usb ( self ): self . conector = Impresión verdadera ( formato CONNECTED . ( POWER_ADAPTERS [ self . __name__ ])) def recharge ( self ): if self . conector : para el estado en RECARGA : imprimir ( estado ) más : imprimir ( CONNECT_FIRST . formato ( POWER_ADAPTERS [ self . __name__ ]))class IPhoneAdapter ( FormatAndroid ): def __init__ ( self , mobile ): self . mobile = móvil def recharge ( self ): self . móvil . recargar () def use_micro_usb ( self ): print ( CONNECTED . format ( POWER_ADAPTERS [ "Android" ])) self . móvil . use_lightning ()clase AndroidRecharger : def __init__ ( self ): self . teléfono = Android () self . teléfono . use_micro_usb () self . teléfono . recargar ()clase IPhoneMicroUSBRecharger : def __init__ ( self ): self . teléfono = IPhone () self . phone_adapter = IPhoneAdapter ( auto . teléfono ) self . phone_adapter . use_micro_usb () self . phone_adapter . recargar ()class IPhoneRecharger : def __init__ ( self ): self . teléfono = IPhone () self . teléfono . use_lightning () self . teléfono . recargar ()print ( "Recarga de Android con el cargador MicroUSB." ) AndroidRecharger () print ()print ( "Recarga del iPhone con MicroUSB usando un patrón de adaptador." ) IPhoneMicroUSBRecharger () print ()print ( "Recarga de iPhone con cargador de iPhone" ) IPhoneRecharger ()
pública interfaz ILightningPhone { nula ConnectLightning (); Recarga vacía (); } interfaz pública IUsbPhone { void ConnectUsb (); Recarga vacía (); } AndroidPhone de clase sellada pública : IUsbPhone { bool privado está conectado ; public void ConnectUsb () { esto . isConnected = true ; Consola . WriteLine ( "teléfono Android conectado" ); }public void Recharge () { if ( this . isConnected ) { Console . WriteLine ( "recarga del teléfono Android" ); } else { Consola . WriteLine ( "Conecte primero el cable USB" ); } } } ApplePhone de clase pública sellada : ILightningPhone { bool privado está conectado ; public void ConnectLightning () { esto . isConnected = true ; Consola . WriteLine ( "teléfono Apple conectado" ); }public void Recharge () { if ( this . isConnected ) { Console . WriteLine ( "recarga del teléfono Apple" ); } else { Consola . WriteLine ( "Conecte primero el cable Lightning" ); } } }pública sellada clase LightningToUsbAdapter : IUsbPhone { privado de sólo lectura ILightningPhone lightningPhone ;private bool isConnected ;public LightningToUsbAdapter ( ILightningPhone lightningPhone ) { this . lightningPhone = lightningPhone ; esto . lightningPhone . ConnectLightning (); }public void ConnectUsb () { esto . isConnected = true ; Consola . WriteLine ( "Cable adaptador conectado" ); }public void Recharge () { if ( this . isConnected ) { this . lightningPhone . Recarga (); } else { Consola . WriteLine ( "Conecte primero el cable USB" ); } } }public void Main () { ILightningPhone applePhone = nuevo ApplePhone (); IUsbPhone adapterCable = nuevo LightningToUsbAdapter ( applePhone ); adaptadorCable . ConnectUsb (); adaptadorCable . Recarga (); }
Producción:
Teléfono de Apple conectado.
Cable adaptador conectado.
Recarga del teléfono Apple.
Wikimedia Commons tiene medios relacionados con Patrón de adaptador . |
Los patrones de diseño de ciencias de la computación de Wikibook tienen una página sobre el tema de: Implementaciones de adaptadores en varios idiomas. |