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.
Descripción general
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]
- ¿Cómo se puede reutilizar una clase que no tiene una interfaz que requiere un cliente?
- ¿Cómo pueden trabajar 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 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:
- Defina una
adapter
clase separada que convierta la interfaz (incompatible) de una clase (adaptee
) en otra interfaz (target
) que requieran los clientes. - Trabaje a través de una
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.
Definició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.
Uso
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. |
Estructura
Diagrama de clases UML
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
:
- La
object adapter
forma implementa latarget
interfaz delegando a unadaptee
objeto en tiempo de ejecución (adaptee.specificOperation()
). - La
class adapter
forma en que implementa latarget
interfaz heredando de unaadaptee
clase en tiempo de compilación (specificOperation()
).
Patrón de adaptador de objeto
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 .
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 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]
Otra forma de patrón de adaptador de tiempo de ejecución
Motivación de la solución de tiempo de compilación
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 .
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,
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 (); } }
- Escriba una clase de adaptador que devuelva la implementación específica del proveedor:
La clase pública ClassAFormat1Adapter extiende el adaptador { public Object adapt ( objeto final anObject ) { return new ClassAFormat1 (( ClassA ) anObject ); } }
- Registre el
adapter
con un registro global, de modo queadapter
se pueda buscar en tiempo de ejecución:AdapterFactory . getInstance (). registerAdapter ( ClassA . clase , ClassAFormat1Adapter . clase , "formato1" );
- En el código, cuando desee transferir datos de
ClassA
aClassB
, 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 ());
- La ventaja se puede ver en que, si se desea transferir los datos en un segundo formato, busque el adaptador / proveedor diferente:
Adaptador adaptador = AdapterFactory . getInstance () . getAdapterFromTo ( ClassA . clase , StringProvider . clase , "formato2" );
- Y si se desea generar los datos
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 ());
- De esta manera, el uso de los adaptadores y los proveedores permite múltiples "vistas" por
ClassB
yClassC
enClassA
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.
Implementación del patrón adaptador
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
Java
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" ); } } } / * exponer 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 MicroUsbMicroUsb conectadoRecarga iniciadaRecarga finalizadaRecargar iPhone con LightningRayo conectadoRecarga iniciadaRecarga finalizadaRecarga de iPhone con MicroUsbMicroUsb conectadoRayo conectadoRecarga iniciadaRecarga finalizada
Pitón
"" " 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 ()
C#
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.
Ver también
- Adaptador de patrones de diseño de Java: adaptador
- Delegación , muy relevante para el patrón de adaptador de objeto.
- Principio de inversión de dependencia , que se puede considerar como la aplicación del patrón de adaptador, cuando la clase de alto nivel define su propia interfaz (adaptadora) al módulo de bajo nivel (implementado por una clase de adaptado).
- Arquitectura de puertos y adaptadores
- Calce
- Función de envoltura
- Biblioteca de envoltorios
Referencias
- ^ a b Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy ; Bates, Bert (2004). Patrones de diseño de Head First . O'Reilly Media . pag. 244. ISBN 978-0-596-00712-6. OCLC 809772256 . Archivado desde el original ( tapa blanda ) 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. 139ff . ISBN 0-201-63361-2.
- ^ "El patrón de diseño del adaptador: problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Freeman, Eric; Freeman, Elisabeth; Sierra, Kathy; Bates, Bert (2004). Hendrickson, Mike; Loukides, Mike (eds.). Head First Design Patterns (rústica) . 1 . O'Reilly Media . págs. 243, 252, 258, 260. ISBN 978-0-596-00712-6. Consultado el 2 de julio de 2012 .
- ^ "El patrón de diseño del adaptador - Estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .