22 de abril de 2017

Patrones de diseño - El puente

Artículo original: Design Patterns - The BRIDGE
http://weblogs.foxite.com/andykramek/archive/2006/12/10/3009.aspx
Autor: Andy Kramek
Traducido por: Ana María Bisbé York


Como he prometido, comienzo una pequeña serie sobre Patrones de diseño, el primero a mirar es el "Puente" - frecuentemente referenciado como la "madre de los patrones de diseño", porque es el patrón de diseño más básico y usted va a descubrir que es más familiar con los patrones en general, cuando encuentre el puente (de alguna forma) como base de casi todos los otros patrones.

OK, entonces, ¿Qué es un puente?

La definición formal de un puente, dada en "Design Patterns, Elements of Reusable Object-Oriented Software" por Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides es que un Puente es:

"Desacoplar una abstracción de su implementación de tal forma que las dos puedan variar independientemente."

Impresionante, ¿eh? ¿Hacemos esto realmente en nuestro código? La respuesta corta es que probablemente nosotros debemos hacerlo más de lo que nos damos cuenta. Vamos a tomar un ejemplo común.

Al escribir código en nuestros formularios y clases queremos naturalmente atrapar las cosas que pueden salir mal y usualmente (considerándonos desarrolladores) queremos decir a los usuarios cuándo van a ocurrir. La solución más obvia y sencilla, es incluir un par de líneas de código en el método apropiado. El resultado puede verse así:

IF NOT <Una función que devuelve True/False>
  lcText = "Chequeo fallido para devolver el valor correcto" + CHR(13)
  lcText = lcText + "Presione cualquier tecla para re-entrar el valor"
  WAIT lcText WINDOW
  RETURN .F. 
ENDIF

En toda nuestra aplicación pueden existir docenas de situaciones donde mostramos una ventana del tipo Wait, para mantener al usuario al corriente de lo que está ocurriendo, y esto funciona perfectamente bien hasta que ocurre una de estas dos cosas. Cualquiera de nuestros usuarios decide que realmente odia estas molestas ventanas de ligera espera "wait windows" y preferirá una ventana estilo "message box", o peor, tendremos que implantar el código en un entorno que no soporta una ventana tipo "wait windows". Es posible lograrlo con un componente COM, o en un formulario Web. Ahora podemos ir y buscar cada ocurrencia de este código por nuestra aplicación, para cambiarla para que garantice el nuevo requerimiento. No se usted; pero en mi experiencia, las posibilidades de hacer esta tarea correcta la primera vez (no olvide ninguna ocurrencia y re-codifique cada una perfectamente) están tan cerca de cero que no se pueden distinguir del mismo cero.  Incluso, si confiamos en que nuestros resultados son correctos, tendremos que hacer un grupo de verificaciones para asegurarnos.

Otro caso es cuando estamos trabajando con colegas. Suponga que en uno de mis formularios llamo a una función del sistema y, siendo un desarrollador cuidadoso, verifico que el resultado es el esperado, y en caso de error, el código muestra un mensaje del tipo:

lcText = "El valor devuelto por la función es incorrecto"

Mientras tanto, mi compañero, escribiendo un pedazo de código similar, en otra parte de la aplicación, escribe sus validaciones de la llamada a la misma función de esta forma:

lcText = "No se ha completado la tarea requerida"

Entonces, incluso si ambos mostramos el texto de igual forma, el contenido es diferente, incluso pensando en el usuario final, está ocurriendo lo mismo ! ¿Es confuso? Puede aportar. Peor, no se ve pulido ante el usuario final.

Entonces, ¿qué tiene que ver esto, con el patrón Puente? Bueno, la razón de que tenemos ese problema es porque hemos fallado al reconocer que estábamos acoplando una abstracción (mostrando un mensaje al usuario) y su implementación (la ventana "Wait window" o "Messagebox"). Si hubiéramos hecho un puente en su lugar, hubiéramos evitado el problema. Así es como se muestra el mismo código si lo hubiéramos implementado, utilizando un patrón de puente.

IF NOT <Una función que devuelve True/False>
  This.loMsgHandler.ShowMessage( 9011)
  RETURN .F.
ENDIF

¿Ve la diferencia? No sabemos nada más, ni nos preocuparemos, por saber qué mensaje se mostrará en realidad o cómo se manipulará. Todo lo que necesita conocer es dónde tomar una referencia al objeto que va a manipular el mensaje por nosotros, y el identificador que dice al controlador qué mensaje deseamos mostrar. Por supuesto,es un requerimiento fundamental que todos los posibles controladores implanten la interfaz adecuada, en este caso el método ShowMessage().

Es este el origen de la referencia, de lo que es un "puente". En este ejemplo la propiedad "oMsgHandler" proporciona el puente entre el código que requiere un mensaje y el mecanismo para manipularlo. Ahora, todo lo que necesitamos es cambiar la forma en que nuestro mensaje es manipulado, para cambiar el objeto de referencia guardado en esta propiedad. Esto es algo que pudo haberse hecho incluso en tiempo de ejecución, dependientemente del entorno en el que el objeto padre se haya instanciado( y el mecanismo para hacerlo es un ejemplo de otro patrón llamado "Estrategia", que vamos a ver después. Esta estrategia desacopla exitosamente la abstracción de la implementación y, como resultado, nuestro código, es mucho más reutilizable.

¿Cuáles son los componentes de un puente?

Un puente tiene dos componentes esenciales, la "abstracción", que es el objeto responsable de inicializar una operación, y la "implementación", que es el objeto que la lleva a cabo. La abstracción conoce su implementación porque guarda una referencia a la misma, o porque lo posee (por ejemplo, lo contiene). Observe que frecuentemente ocurre que la abstracción crea la implementación, que no es un requerimiento absoluto del patrón. Un puente también podría hacer uso de una referencia ya existente para una misma implementación de objeto. De hecho, diseñar puentes de esta forma, puede ser muy eficiente porque diferentes objetos abstractos pueden utilizar el mismo objeto implementación. Debido a que Visual FoxPro tiene un modelo de contenedores muy bueno, la mayoría de los desarrolladores notarán que lo han utilizado (no intencionalmente) para implementar Puentes con más frecuencia de lo que imaginan. La mayoría de los objetos "administradores" ( por ejemplo: El Administrador de Formularios, El Administrador de archivos, el Administrador de mensajes), son en realidad ejemplos de patrón de Puente.

Sin embargo, no confunda el envío de mensajes con el puente. Si un método de un botón de comandos llama a un método en su formulario padre, esto implementa directamente la acción necesaria, que es enviar un mensaje, no es un puente. Sin embargo, si el método del formulario va a llamar a otro objeto para que lleve a cabo la acción, entonces tenemos un puente.

Es interesante, otra posibilidad que ocurre cuando un formulario (u otro contenedor) tiene una propiedad que guarda una referencia a un objeto de implementación. Un objeto contenedor (por ejemplo un botón de comandos en un formulario) puede acceder a esta propiedad directamente y tomar una referencia local al objeto de implementación. Puede entonces, llamar a los métodos del objeto de implementación directamente. Ahora, ¿este es un puente, o un envío de mensajes? De hecho, probablemente podría argumentar ambos lados del caso con igual justificación. Sin embargo, la respuesta en realidad no importa, este asunto surge sólo porque Visual FoxPro expone las propiedades como "Públicas" de forma predeterminada, y también implementa punteros hacia atrás, los que permiten a los objetos dirigirse a sus padres directamente. En la mayoría de los entornos orientados a objeto, es sencillamente imposible para los objetos comportarse tan toscamente entre ellos.

Entonces, para implementar un puente necesita identificar qué objeto va a realizar la función de abstracción y cuál la implementación. Una vez que lo haya definido, todo lo que queda por decidir es cómo el puente entre los dos será codificado y esto dependerá enteramente del escenario.

No hay comentarios. :

Publicar un comentario