30 de abril de 2017

Patrones de diseño - La cadena de responsabilidad

Artículo original: Design Patterns - The Chain of Responsibility
http://weblogs.foxite.com/andykramek/archive/2006/12/24/3060.aspx
Autor: Andy Kramek
Traducido por: Ana María Bisbé York


¿Qué es una cadena de responsabilidad y cómo utilizarla?

En mi artículo anterior hemos constatado que un patrón de estrategia describe una solución del problema de tratar con implementaciones alternativas en tiempo de ejecución. La cadena de responsabilidades es otra vía de afrontar básicamente el mismo problema.

¿Cómo puedo reconocer cuando necesito una cadena de responsabilidad?

La definición formal de cadena de responsabilidad dada por “GoF” es:

Evitar el acoplamiento del emisor del requerimiento y el receptor, al obtener más de un objeto con posibilidad de tratar el requerimiento. Es la cadena de objetos que reciben y pasan la petición a lo largo de la cadena hasta que un objeto la manipule.

En el ejemplo previo mostramos como utilizar una estrategia sin que importe el problema de aplicar una localización específica de tarifas de impuesto de ventas en tiempo de ejecución. Sin embargo, como hemos visto, para implementar una estrategia de algunos objetos, en algún lugar tiene que decidir, en tiempo de ejecución, cuáles son las posibles subclases a ser implementadas. Esto puede no ser siempre deseado, o incluso, posible.

En una cadena de responsabilidad cada objeto sabe cómo evaluar la petición para una acción y, si no la puede controlar por si mismo, sabe solamente cómo pasarla a otro objeto, por ello la “cadena”. La consecuencia de esto es que el cliente, (que inicia la petición de la acción) ahora sólo necesita conocer sobre el primer objeto en la cadena. Además, cada objeto en la cadena, sólo necesita conocer también sobre un objeto, el siguiente en la cadena. La cadena de responsabilidad puede ser implementada utilizando una cadena predefinida o estática, o las cadenas pueden ser dinámicas, construidas en tiempo de ejecución teniendo cada objeto su propio sucesor cuando sea necesario.

¿Cuáles son los componentes de una cadena de responsabilidad?

Una cadena de responsabilidad puede ser implementada al crear una clase “manipuladora” (handler) abstracta (para especificar la interfaz y funcionalidad genérica) y crear subclases concretas para definir varias posibles implementaciones. Sin embargo, no existen requerimientos absolutos para todos los miembros de la cadena de responsabilidad, por descender a partir de la misma clase, proporcionando que todos ellos soporten la interfaz necesaria para integrar con otros miembros de la cadena.

Los objetos clientes necesitan una referencia a la subclase específica, la cual es su punto de entrada individual a la cadena. (Observe que no todos los clientes necesitan utilizar el mismo punto de entrada).

Nuevamente, vemos el patrón de puente básico. Esto es, porque cada enlace en la cadena es en realidad un puente entre una abstracción y una implementación. La diferencia con un puente sencillo es que un único objeto puede desempeñar varios roles en dependencia con su situación.

De esta manera, el primer enlace tiene el cliente como abstracción y el primer objeto controlador concreto, como la implementación. Sin embargo, el segundo enlace tiene ahora el primer objeto controlador desempeñando el rol de abstracción, que es a su vez, de implementación. En su turno, el segundo enlace se convierte en abstracción para la implementación del tercer enlace. Este patrón puede, en teoría al menos, repetirse hasta el infinito.

Con el objeto de implementar un patrón de cadena de responsabilidades, necesitamos definir primero una clase controladora abstracta y tantas subclases específicas como necesite. La diferencia clave entre la Cadena de responsabilidad y el patrón de estrategia es que la clase abstracta debe definir el mecanismo, por el cual un objeto puede determinar si puede controlar el requerimiento. Adicionalmente necesita una propiedad para guardar una referencia al objeto siguiente en la cadena. Como he mostrado antes, no existen requerimientos absolutos para todos los manipuladores que hereden del mismo manipulador abstracto, proporcionando que ellos adhieran la mínima interfaz definida.

¿Cómo implementar una cadena de responsabilidad?

La pregunta que puse en el contexto del patrón Estrategia fue: "¿Cómo solucionar el problema de calcular impuestos cuando el cálculo depende de la localidad donde se realiza la venta?" Usted recordará que la solución fue definir subclases específicas para calcular cada tipo de impuesto y luego determinar qué subclase es la requerida basándose en una tabla que enlazaba la localidad con el tipo de impuesto aplicable.

Para controlar este mismo problema con una cadena de responsabilidad podemos utilizar la clase para calcular el impuesto original que definimos para el patrón Estrategia y agregarle nuevas propiedades:

  • cCanHandle Define el contexto controlado por esta subclase.
  • cNextObj Nombre de la clase para instanciar como el próximo objeto en la cadena.
  • cNextObjLib Biblioteca de clases para el próximo objeto de la cadena
  • oNext Referencia del objeto para el próximo objeto de la cadena

La propiedad "cCanHandle" define el "contexto" que aceptará la instancia específica. Mientras las "futuras" propiedades se utilizan para definir qué objeto debe seguir a cual. Esto podría ser con un prellenado para crear una cadena predefinida o podría incluso determinar los valores relevantes en tiempo de ejecución para crear una cadena extensible infinitamente que cambie de acuerdo a las necesidades.

Por ejemplo, supongamos que el contexto del primer objeto fue definido como un tipo de impuesto del 7.00% entonces, podría determinar que si ha pasado un contexto que es mayor que sí mismo, entonces no se trata instanciar un objeto con un valor de contexto que sea menor que si mismo. ¿Cómo se implementa esto? Una forma podría ser tener una tabla que liste para cada valor de contexto la clase relevante (y su biblioteca). Entonces, cada objeto en la cadena podría simplemente buscar la información cuando necesite.

Además de las propiedades descritas antes, necesitamos al menos dos métodos:

  • ProcessRequest: Método expuesto que se utiliza para llamar al objeto y que determina si una petición específica es procesada por el objeto.
  • CalcTax: Método real que realiza el cálculo y devuelve el resultado.

El objeto cliente debe crear, u obtener una referencia al primer objeto en la cadena. Entonces, llamará al método ProcessRequest() de ese objeto y pasará el contexto y el precio de venta para el que se ha requerido un impuesto. Este código asume que la cadena de Responsabilidad va a devolver o un Impuesto o un NULL:

WITH ThisForm
  *** Verificar que tenemos disponible el primer objeto de la cadena
  IF VARTYPE( This.oCalc ) # "O"
    *** No lo tenemos, por tanto, lo creamos
    .oCalc = NEWOBJECT( 'cntTaxChain01', 'ch15.vcx' )
  ENDIF
  *** Llamamos al método ProcessRequest() y pasamos el contexto y precio
  lnTax = .oCalc.ProcessRequest( tcContext, tnPrice )
  IF ISNULL( lnTax )
    *** No es posible procesar la petición
    MESSAGEBOX( ‘No es posible procesar esta localidad’, 16, 'Error' )
    lnTax = 0
  ENDIF
  RETURN lnTax
ENDWITH

El código en el método ProcessRequest(), está definido en el nivel de la clase abstracta y es completamente genérico. Es el responsable de determinar si la petición entrante es manipulada localmente. Si es así, llama simplemente el método predeterminado CalcTax() (también definido en la clase abstracta la que usa el precio pasado y la tarifa de impuesto integrada). Si no, la acción depende de si es definido otro objeto, y disponible, para manipular la petición, como sigue:

LPARAMETERS tcContext, tnPrice
LOCAL lnTax
WITH This
  *** ¿Podemos tratar la petición aquí?
  lcCanHandle = CHRTRAN( .cCanHandle, "'", "" )
  IF tcContext = lcCanHandle
    *** Sí, entonces llamamos al método CalcTax(), pasando el precio
    lnTax = .CalcTax( tnPrice )
  ELSE
    *** No podemos tratarla, ¿Hay un objeto definido para pasársela? 
    IF NOT EMPTY( .cNextObj ) AND NOT EMPTY( .cNextObjLib )
      *** Si lo tenemos, pero ya existía antes 
      IF VARTYPE( This.oNext ) # "O"
        *** Crea el objeto y lo llama
        .oNext = NEWOBJECT( .cNextObj, .cNextObjLib )
      ENDIF
      *** Llamo al objeto especificado
      lnTax = This.oNext.ProcessRequest( tcContext, tnPrice )
    ELSE
      *** No hay a donde ir, devolvemos NULL
      lnTax = NULL
    ENDIF
  ENDIF
  RETURN lnTax
ENDWITH

Este es todo el código que es necesario y la subclase individual para este ejemplo sencillo no necesita personalizar código alguno, todo está controlado por propiedades de configuración (incluyendo la propiedad nTaxRate definida en la clase raíz original.)

¿Cuándo debo utilizar una Cadena en lugar de una Estrategia?

En este momento se puede estar preguntando ¿por qué preocuparse? - especialmente, una vez que hemos visto realmente una buena solución controlada por datos a un problema en el patrón de Estrategia. Sin embargo, la limitación del patrón de estrategia es que solamente UNA subclase puede existir, y por tanto no es apropiado cuando necesitamos múltiples operaciones. El ejemplo empleado aquí ¡es para una cadena corta! Tan pronto como un objeto controla la tarea el resultado es devuelto y ningún otro objeto es necesario.

Sin embargo, la Cadena de Responsabilidad realmente logra su máximo rendimiento cuando pueden ser requeridas múltiples operaciones. Una extensión de nuestro sencillo problema de Impuestos podría ser controlar problemas como "Gastos de Envío y Manipulación", "Descuentos" o incluso impuestos múltiples (por ejemplo sobrecargos locales).

En esta situación, una Estrategia no es suficiente; pero la Cadena de responsabilidad controla esto fácilmente. Todo lo que necesita es, en lugar de detenerse ante el primer objeto que puede controlar la tarea, pasamos la petición a cada objeto en la cadena inequívocamente, y permitimos a todos la oportunidad de contribuir a la solución final. Por supuesto, podemos necesitar más de un único valor devuelto en algún escenario; pero los objetos parámetros también son una forma sencilla y conveniente para controlar esto.

Entonces, tendríamos objetos en nuestra cadena basados en clases "Calculadora de Impuestos de venta", "Calculadora de Gastos de envío" y "Calculadora de descuentos". Todo lo que ellos necesitan son las PEMs necesarias para participar en la cadena.

Resumen de patrón de cadena de responsabilidad.

La cadena de responsabilidad nos proporciona otra vía para solucionar el problema de proporcionar la funcionalidad sin la necesidad de programarlo explícitamente. Quizás, la mayor ventaja de la cadena de responsabilidades es la facilidad con que puede extenderse, mientras su inconveniente fundamental es que, puede crecer dramáticamente la cantidad de objetos activos en el sistema. Como siempre, termino recordándole que siempre debe pensar que la implementación actual de detalles puede cambiar, el patrón como tal, no cambia.

28 de abril de 2017

Patrones de diseño - El decorador

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


El decorador describe una solución al problema de agregar una funcionalidad a un objeto sin cambiar realmente nada del código en el objeto.

¿Cómo reconozco cuándo necesito un decorador?

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

Adjuntar responsabilidad adicional a un objeto dinámicamente.

La necesidad de cambiar dinámicamente la funcionalidad o comportamiento de un objeto surge típicamente en una de estas dos situaciones. Primero, cuando el código fuente del objeto no está disponible, puede ser que el objeto sea un control ActiveX o una biblioteca de clases de terceras partes que estemos utilizando. Segundo, cuando la clase es ampliamente utilizada; pero la responsabilidad específica, se necesita sólo en una o más situaciones particulares y pueda ser inapropiado agregar el código a la clase.

En los artículos precedentes hemos visto varias vías para determinar la cantidad de la tasa a aplicar al 'precio' en función de la 'localidad'. El objeto para el cálculo básico en cada caso trabaja exponiendo un método llamado CalcTax(), que toma dos parámetros, un valor y un tipo de impuesto y devuelve la tasa calculada. Afrontamos el problema de tratar con la tasa en el formulario, utilizando un patrón de puente para separar la implementación de la interfaz. Sin embargo, como hemos visto rápidamente, esto no fue suficientemente flexible para asociar diferentes índices de tasa a diferentes localidades. Ambos, los patrones de estrategia y de cadena de responsabilidad pueden manipular este aspecto, sin embargo, ambas soluciones implican crear subclases de nuestra clase base de cálculos de tasas.

El patrón decorador nos permite solucionar el mismo problema sin necesidad de crear subclases. En lugar de definir un nuevo objeto, que tiene exactamente la misma interfaz como el cálculo de la tasa, pero que incluye además el código necesario para determinar la tasa apropiada para una localización dada. El objeto "mira" justo como el cálculo de la tasa como su cliente; pero, porque también guarda una referencia al objeto de cálculo de tasa, puede "pre-procesar" cualquier requerimiento de un índice y luego, simplemente puede dar la implementación real cuando esté lista.

¿Cuáles son los componentes del decorador?

Un decorador, tiene dos requerimientos esenciales. Primero, necesitamos la clase implementación que define la interfaz y el núcleo de funcionalidad. Segundo, necesitamos un decorador que reproduzca la interfaz de implementación, y guarde una referencia a el. El objeto cliente ahora dirige las llamadas que podrían ir directamente a la implementación, en su lugar, del objeto decorador. La estructura básica del patrón decorador es:

Este patrón es en realidad un puente extendido. Debido a que el cliente está afectado, puede dirigir el objeto decorador como si realmente fuera la implementación al final del puente estándar, porque la interfaz de los dos es la misma. Debido a que la implementación está afectada, la petición mira exactamente igual que si fuera directamente desde el cliente. En otras palabras, no necesita nunca conocer incluso que el decorador existe.

¿Cómo implementar un decorador?

Una nueva clase, nombrada "cntDecorador" ha sido definida para implementar el ejemplo del decorador. Tiene tres propiedades y un método que se muestran a continuación:

NombreDescripción
cImpclassNombre de la clase a instanciar para este decorador.
cImplibBiblioteca de clases para la implementación de la clase a instanciar.
oCalculatorReferencia de objeto para instanciar la clase que es el implementador real al método CalcTax(). Este objeto es el instanciado en el Init() del decorador.
CalcTaxImplementación del decorador para el método equivalente en el implementador 'real'

La clase decorador es realmente muy simple. Al crearse, instancia una clase implementación real, la cual está definida por sus propiedades: nombre de clase y biblioteca. Además implementa un método operacional (en este caso el método CalcTax()), nombrado de la misma forma y que tiene la misma estructura que el método real a implementar.

El código en el método CalcTax() del decorador está muy claro. Espera recibir dos parámetros, el ID de localización como una cadena, y el precio para el que la tasa es requerida. Observe que éstos no son los mismos parámetros que se requieren por el método CalcTax() en el método real. (Aquí necesitamos pasar un precio y el índice de la tasa para aplicarle). El método CalcTax() del decorador determina el índice adecuado basándose en el ID de localización y luego llama al método CalcTax() de su objeto de implementación pasando el parámetro 'real'. El valor devuelto es justamente pasado al cliente sin ninguna modificación adicional.

LPARAMETERS tcLocation, tnPrice
LOCAL lnRate, lnTax
STORE 0 TO lnRate, lnTax

*** Determine el índice correcto
DO CASE 
  CASE tcLocation = '01'
    lnRate = 5.75
  CASE tcLocation = '02'
    lnRate = 5.25
  CASE tcLocation = '03'
    lnRate = 0.00
  OTHERWISE
    lnRate = 0
ENDCASE
*** Ahora, pase la llamada al objeto real
lnTax = This.oCalculator.CalcTax( tnPrice, lnRate )
*** Y devuelva el resultado
RETURN lnTax

Para utilizar el decorador, apenas necesitamos un pequeño cambio en el cliente. En lugar de instanciar el objeto real para el cálculo, en su lugar instancia el objeto decorador. Sin embargo, recuerde que la forma de la llamada para el método decorador es idéntico al método 'real', por lo que no hace falta ningún cambio.

LPARAMETERS tcContext, tnPrice
LOCAL lnTax
WITH ThisForm
  *** Verifica que tenemos disponible el Decorador
  IF VARTYPE( This.oCalc ) # "O"
    *** No lo tenemos, entonces lo creamos
    .oCalc = NEWOBJECT( 'cntDecorator', 'ch15.vcx' )
  ENDIF
  *** Ahora, sólo llamamos su método CalcTax() 
  *** y pasa ambos parámetros: localidad y precio
  lnTax = .oCalc.CalcTax( tcContext, tnPrice )
  IF ISNULL( lnTax )
    *** No puede procesar la petición
    MESSAGEBOX( 'No puede procesar la localización', 16, 'Error' )
    lnTax = 0
  ENDIF
  RETURN lnTax
ENDWITH

Aunque es un ejemplo muy sencillo, puede ver qué fácil puede ser extender la funcionalidad del decorador. Un problema muy frecuente, en la vida real, en que este patrón puede ser utilizado, es cómo proporcionar la implementación de validación específica a una rutina genérica de Guardar.

Resumen de patrón de decorador

El patrón de decorador es utilizado cuando deseamos modificar el comportamiento básico de una instancia específica sin la necesidad de crear una nueva subclase, o cambiar el código en el original. El decorador, esencialmente actúa como un preprocesador para su implementación interponiendo su cliente y la implementación.

25 de abril de 2017

Patrones de diseño - La estrategia

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


¿Qué es una estrategia y cómo la utilizo?

La estrategia describe una solución al problema principal inherente a escribir el código principal – qué hacer con demandas inesperadas para cambios de implementación.

¿Cómo reconozco dónde necesito una estrategia?

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

Define una familia de algoritmos, encapsula cada uno, y los hace intercambiables. La estrategia permite al algoritmo variar en dependencia del cliente que la utilice.

Esto resulta algo oscuro en una primera lectura, vamos a verlo en un ejemplo donde el patrón de estrategia puede ayudar.

Considere el problema de calcular la tasa de impuestos debido por una compra. Podemos asumir que nuestra aplicación conoce los elementos adquiridos, y la cantidad y entonces (a partir de su información de precios) puede determinar el valor de la venta. Ahora tenemos que aplicar la tarifa de impuesto.

Esto no es un problema, si tenemos un entorno donde el impuesto de venta se aplica a una única tarifa independientemente de la localidad ( como con el VAT en el Reino Unido, por ejemplo). El único problema aquí es saber si el elemento tiene impuesto, y si lo tiene cuál es la tarifa actual. Sin embargo, en EEUU, ¡ los impuestos sobre las ventas son locales ! La tarifa aplicada depende incluso de dónde compramos algo (o incluso, dónde reside el vendedor.)

Por ejemplo, en nuestra localidad, la tasa de ventas en ropa es 5.75%; pero si viajamos 20 millas al sur, la tasa base es sólo del 5.25%, mientras, que si viajamos dentro del mismo estado (sólo a 50 millas) no existen tasas para vestuario de ningún tipo. El mismo elemento de vestuario etiquetado como $29.95 puede, sin embargo, costarnos $31.67, $31.52 o $29.95 según donde compremos. Teniendo esto en cuenta para cualquier otro elemento, la tasa aplicable a una transacción depende del contexto (la combinación específica de tipo de elemento y localización), en el cual tiene lugar la transacción. Si hubiéramos escrito el código en la aplicación para controlarlo podríamos comenzar con algo como esto:

DO CASE
  CASE lclocale = "Akron"
    IF lcItemType = "Ropa"
      lnTaxRate = 5.75
    ELSE
      *** Otros elementos aquí
    ENDIF
  CASE lclocale = "Canton"
    IF lcItemType = "Ropa"
      lnTaxRate = 5.25
    ELSE
      *** Otros elementos aquí
    ENDIF
  CASE lclocale = "Grove City"
    IF lcItemType = "Ropa"
      lnTaxRate = 0.00
    ELSE
      *** Otros elementos aquí
    ENDIF
  OTHERWISE
    *** Aplicar un valor predeterminado
    lnTaxRate = 5.50
ENDCASE

Podemos ver inmediatamente que surgirá un problema. Ocurrirá, cuando necesitemos agregar "Cleveland" a nuestra lista de localidades, o cuando Akron, desalentado por las pérdidas en ventas de Canton, recorte sus tasas para vestuario. Necesitaremos cambiar nuestro código con todos los riesgos que conlleva. Más adelante, como vamos a aumentar la cantidad de localidades, este código será inmanejable. Por supuesto, podríamos guardar esta información en una tabla (una columna para localidad, y quizás, una columna para cada elemento) y revisarlo cada vez que sea necesario. De esta forma, lo único que tenemos que hacer es, modificar registros cuando cambie el dato; pero la tabla podría crecer rápidamente y puede haber muchos datos duplicados y redundancia de los datos, lo cual no es tampoco la solución ideal. Lo que queremos realmente que haga nuestra aplicación es que sea capaz de pasar el precio del ticket, y la información de la localidad, a algo que nos diga qué tasa debemos aplicar. En otras palabras deseamos separar la abstracción (calcular el impuesto debido) de la implementación (basada en la localidad).

Sin embargo, sin cambiar el código, lo cual es re-localizar; pero no cambiar el problema, un puente no es la solución porque solo permite una única implementación y nosotros necesitamos múltiples implementaciones. Necesitamos ser capaces de decidir, en tiempo de ejecución, qué implementación necesita. El patrón de estrategia nos permite definir clases para cada situación que debemos tratar y luego instanciar la adecuada en tiempo de ejecución.

¿Cuáles son los componentes de una estrategia?

Una estrategia tiene tres componentes esenciales: "la estrategia abstracta", que es una clase que define la interfaz, y funcionalidad genérica para las "estrategias concretas", que son subclases que definen varias implementaciones posibles. El tercer componente, el "Contexto", es responsable de controlar la referencia a la implementación actual. La estrategia es inicializada por una petición de acción del cliente.

Puede pensar, que una estrategia es como un "puente dinámico" en el cual un final (el contexto) es estático; pero el otro (la estrategia) está creado por varias variantes con el propósito de entregar una implementación específica en un momento específico. La esencia del patrón es tal que la decisión como establecer qué subclase debe ser instanciada en cualquier momento dependa de la información derivada del cliente (por ejemplo, la aplicación).

Este patrón generalmente basa la responsabilidad de instanciar lel objeto estrategia concreto en el cliente, que pasa una referencia al contexto. Para implementar un patrón de estrategia, usted necesita definir la clase de estrategia abstracta y cuántas subclases especificas necesita. El objeto, que va a jugar el rol de contexto (en VFP es típicamente el formulario, o contenedor padre), necesita exponer una interfaz apropiada a sus clientes potenciales y además, requiere una propiedad que es utilizada para guardar la referencia actualmente activa de implementación de objeto.

¿Cómo implemento una estrategia?

El siguiente ejemplo ilustra cómo se puede utilizar un patrón de estrategia para implementar una solución a los impuestos de venta que hemos discutido en el preámbulo de este tópico.

Lo primero que necesitamos hacer es definir una clase abstracta que tenga una propiedad para el impuesto aplicable. Tiene además, métodos para calcular el impuesto utilizando el valor pasado a la propiedad. Luego creamos tantas subclases de esta definición como tarifas de impuestos trabajamos - una subclase para cada tarifa.

Ahora necesitamos considerar cómo decidimos cuál de varias subclases necesitamos en cada momento dado. Para determinar esto, necesitamos relacionar las diferentes localidades y las tarifas aplicables (la "localidad") a su subclase apropiada. Existen, por supuesto, muchas formas en que podemos hacer esto y la más sencilla es definir la lista de localidades (por ejemplo, ciudades) y la tarifa de impuesto que se aplica en cada una. Una vez que tenemos una subclase específica para cada tarifa de impuesto, podemos derivar la subclase necesaria para cada localidad.

Aunque aun necesitamos una tabla, un registro para cada ciudad, podemos reducir la duplicación ya que nombramos las clases por la tarifa que aplica. El resultado es algo como esto:

CiudadTarifaSub-clase
Akron5.75Tax575
Canton5.25Tax525
Grove City0.00Tax000
Kent5.75Tax575

En otras palabras, utilizamos la tarifa aplicable para identificar las subclases que necesitamos con la consecuencia de que no necesitamos la tercera columna de la tabla - podemos inferirlo directamente de la Tarifa.

Entonces, ¿cómo podemos programar esto en un formulario? Bien, es muy sencillo. Todo lo que necesitamos es una lista desplegable de las localidades basada en nuestra tabla Ciudad/Tarifa (City/Rate). Cuando se selecciona la ciudad podemos comprobar la imposición fiscal aplicable. Teniendo esto, podemos derivar el nombre de la subclase a instanciar, y debido a que todas las subclases derivan de un padre común sabemos que todo lo que necesita es llamar al método adecuado del objeto, pasando los parámetros esperados - en este caso, dos parámetros - la clave contextual (la que define la tarifa aplicable) y el "precio" al que se calcula el impuesto. La clave contextual es usada para generar el nombre de la subclase requerida. Si la clase instanciada actualmente no es la correcta, simplemente instanciamos la correcta, como sigue.

LPARAMETERS tcContext, tnPrice
WITH ThisForm
  *** Define el nombre del objeto de la etrategia
  lcStrategy = "CNTTAXSTRAT" + PADL( tcContext, 2, '0' )
  *** Ya está ahi
  IF ISNULL( .oStrategy ) OR NOT UPPER( .oStrategy.Name ) == lcStrategy
    *** Necesitamos crear este
    .oStrategy = NEWOBJECT( lcStrategy, 'Ch15.vcx' )
  ENDIF
  *** Ahora, es sólo llamar al método CalcTax() method y pasar el precio
  lnTax = .oStrategy.CalcTax( tnPrice )
  RETURN lnTax
ENDWITH

Aunque esta es una forma en que puede ser implementada una estrategia y tiene sentido en este escenario, otros escenarios pueden requerir diferentes implementaciones. Por ejemplo, considere la aplicación de descuentos en una orden. La orden de entrada del formulario (el contexto) puede necesitar la aplicación de diferentes tipos de descuentos a una orden:

  • Descuento al valor del elemento, aplicado a una línea de un elemento cada vez.
  • Descuento del valor de la orden, aplicado al valor total de la orden.
  • Elemento de descuento especial o promoción.
  • Descuento a clientes, aplicado al valor de la orden basado en el usuario

Una posible interfaz utilizará botones de comandos (los clientes), de tal forma que el operador pueda determinar cuándo aplicar un tipo de descuento específico. En este escenario puede ser enteramente apropiado, y mucho más simple, que cada botón sea el responsable de instanciar la subclase de estrategia del descuento apropiado y almacenar su referencia a la propiedad del formulario. (Recuerde que mientras podemos hacer este tipo de cosas fácilmente en Visual FoxPro, otros lenguajes tendrían que pasar una referencia explícitamente) El formulario puede manipular el cálculo actual y ocuparse de que el resultado sea tal que el código no tenga que ser duplicado en cada botón.

Resumen de patrones de estrategia.

Hemos mostrado una implementación posible del patrón de estrategia y esbozado otro. La ventaja de la estrategia es tal que evita la necesidad de tener que incrustar implementaciones alternativas en el código permitiéndonos crear objetos separados, que van a compartir una interfaz común, para cada opción y solamente instanciar el objeto necesario en tiempo de ejecución. Como otros patrones los detalles de la implementación pueden variar con las circunstancias pero el patrón como tal no cambia.

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.

19 de abril de 2017

Análisis y Diseño Orientado a Objetos. Enfoque iterativo

Autor:
Jim Booth

Texto original:
When Does It Happen?

Traducido por:
Ana María Bisbé York


Introducción

El desarrollo de sistemas orientados a objeto es nuevo para muchos de nosotros. Ser nuevo en esto supone muchos cambios. Entre ellos está el proceso de planificación y dirección del proyecto. La mayoría de nosotros ha desarrollado proyectos antes. En aquellos proyectos trabajábamos con los usuarios para saber cuál era el sistema y cómo era que debían hacerse las cosas. Nosotros diseñábamos los programas y escribíamos las aplicaciones. Entonces, ¿qué tiene de especial la orientación a objetos?

Uno de los aspectos de la orientación a objetos es el hecho de que es relativamente nuevo de usar. Debido a esta novedad estamos casi constantemente encontrando barreras de ideas a superar. Este escrito está diseñado para exponer uno de los muchos aspectos del desarrollo orientado a objetos, el análisis y diseño y para ayudarnos a encontrar el “sendero en la jungla”. El método de análisis y diseño que vamos a examinar es llamado Enfoque iterativo, toma este nombre porque todo el proceso de desarrollo es logrado a través de una serie de iteraciones donde cada una abarca el proceso entero para el análisis a través de pruebas. Durante cada una de estas iteraciones somos capaces de retroalimentar la información de las primeras etapas del proyecto.

¿Cuándo debemos usar un proceso interactivo?

Martin Fowlr, en su libro “UML Distilled" (disponible en www.amazon.com), dice”Debe utilizar un desarrollo iterativo solo en los casos en que desee obtener éxito” El no está exagerando. Nadie puede asegurar completamente ninguna fase del ciclo de desarrollo en un simple paso. Estos son simplemente muchos detalles a los que dirigirse en cada etapa de desarrollo. La metodología puede ser usada tal que no solo permita revisar las etapas anteriores sino que también las complemente. La metodología iterativa es justo eso. Requiere que grandes proyectos sean partidos en pequeñas piezas y que cada pieza sea desarrollada mediante un proceso iterativo de análisis, diseño, implementación y pruebas.

¿Por qué hacer iteraciones?

Las iteraciones nos permiten enfocar un subconjunto del proyecto completo de tal forma que lo podemos terminar en detalle. Frecuentemente vamos a descubrir nuevos problemas y requerimientos durante el proceso de creación de uno de sus subsistemas. Estos nuevos descubrimientos pueden ser fácilmente incorporados en una iteración posterior sin desechar lo que se ha avanzado hasta entonces.

Este proceso nos permite probar cada subsistema independientemente y asegurar su propia funcionalidad. Esto significa que cuando alcancemos la etapa final del desarrollo - la integración entre los subsistemas como un todo - podremos concentrarnos en la integración sabiendo que cada subsistema está ya completamente probado.

Trabajando lo más temprano posible con los aspectos de alto riesgo para el proyecto, seremos capaces de reducir la influencia de estos riesgos en el cronograma completo del proyecto.

Durante la implementación de los subsistemas nuevos casos de uso pueden ser descubiertos. Estas situaciones nuevas pueden ser planificadas para la siguiente iteración.

¿Qué es el enfoque iterativo?

El enfoque iterativo no es nada nuevo ni revolucionario. Muchos de nosotros hemos creado sistemas por esta vía hace mucho tiempo. Martin Fowler clasifica las fases de un proyecto iterativo como Iniciación, Elaboración, Construcción y Transición. Cada una de estas fases constituye un punto diferente en la continuidad del proyecto hasta el final del mismo.

El proyecto comienza con la fase de Iniciación. Durante esta fase, la que discutiremos con más detalle más adelante, vamos a invertir un tiempo explicándonos qué es el proyecto y la mejor idea de cómo hacerlo. Al final de la fase de iniciación debemos tener una idea bastante acertada del alcance del proyecto. Los detalles no serán obtenidos; pero la visión general del sistema está aquí. En este punto podemos hacer nuestro primer corte del proyecto en piezas. Estas piezas deben estar suficientemente encapsuladas que permitan crearlas independientemente. Cada una de estas piezas satisface un subconjunto de requerimiento del sistema completo.

La mayor ventaja de este método es que puede identificar el riesgo involucrado con el proyecto y tenerlo en cuenta en la dirección del mismo. Esto nos ayuda a evitar, que conociendo todas las cosas que podrían ir mal haya que esperar a que empiecen a fallar.

Estos riesgos pueden ser esparcidos por todo el proyecto y continuar teniéndolos en cuenta.

Categorías de riesgo

Los riesgos que enfrentamos en el desarrollo del proyecto los podemos dividir en cuatro categorías. Estas categorías son: riesgos de requerimiento, riesgos tecnológicos, riesgos de habilidades y riesgos políticos. Cualquier proyecto con un alcance relativo tendrá algunos riesgos asociados con cada una de estas características. Ignorar o negar la presencia de estos riesgos significaría matar el proyecto. Estos riesgos pueden ser solo superados si no son bien manejados. Manejar los riesgos requiere de conocer cual es el riesgo y tener un plan para lidiar con ellos.

Miremos las categorías de riesgo

Riesgos de Requerimientos

La categoría de riesgos de requerimientos incluye aquellas cosas que ponen en riesgo el proyecto desde la temprana etapa de descubrimiento de requerimientos del sistema. Podemos crear el mejor sistema de software que nadie ha visto jamás para manipular la logística del papel baño de las fuerzas armadas de los EUA; pero si la necesidad fue administrar la afiliación del club de oficiales su proyecto será un fracaso.

Estar seguro de que estamos creando el proyecto correcto, este es el logro principal de administración de requerimiento de riesgos. Se generarán menores costos en la medida que se reconozcan estos riegos lo antes posible en el proyecto. Solo imagine la cantidad de trabajo (y costo) perdido si nosotros descubrimos sobre el club de oficiales en el día que vamos a instalar la aplicación logística.

El uso de casos es imprescindible en la administración de riesgos de requerimiento. El uso de casos predecirá el comportamiento que será alcanzado por el sistema. Esto le va a dar una clara visión de cómo el sistema y los usuarios interactúan.

Desarrollar un dominio conceptual de los negocios. El dominio conceptual debe darle una clara comprensión de qué cosas y en qué negocios interactúan. Un concepto general de cómo los negocios trabajan.

Luego, puede combinar el uso de casos con el modelo de dominio para crear el modelo de diseño. Tomar tiempo para identificar la información necesaria para crear estos modelos le dará una detallada información de los usuarios y los expertos de negocios sobre el proyecto. Sin dudas, usted reducirá los riesgos de requerimiento considerablemente.

Riesgos Tecnológicos

El primer riesgo tecnológico viene al olvidar la relación con la orientación a objetos. ¿Es la Orientación a Objetos un nuevo paradigma para usted o su equipo? Si es así, entonces el riego tecnológico aquí es bastante grande. ¿Enfrentará ese riesgo? ¿Va a disponer de presupuesto para entrenarse en estas técnicas, usted y su equipo? ¿Se puede permitir tener un consultante que esté disponible para dudas, reuniones, y revisiones periódicas del sistema?

¿Qué hay del problema con las nuevas tecnologías? Este nuevo proyecto requiere que los informes sean publicados en un sitio web. El sitio web debe permitir criterios dinámicos en los informes. Hey, esto de la web debe ser simpático, ¿no?

Si, mucha diversión en lo que usted comienza tratando de seleccionar el software y hardware verá y encontrará los requerimientos. Así podrá calcular la calidad de varias tecnologías que necesita incorporar para el proyecto rápidamente encontrará que este es el próximo mayor riesgo.

¿Qué ocurre si selecciona el mejor software de servicio web y el mejor software de base de datos remota solo para encontrar que no son compatibles y no trabajan bien juntos? Puede asegurar que cualquier nueva tecnología podría causarle problemas. Aquellos problemas tomarán tiempo de su proyecto.

La solución de estos riesgos tecnológicos es similar a los riesgos de requerimientos, consultores, entrenadores o tutores con habilidades específicas en esas tecnologías.

Otro método para minimizar los riesgos tecnológicos en el proyecto es crear prototipos. Crear muchos prototipos, especialmente para aquellos aspectos del sistema que usan las tecnologías más avanzadas y menos entendidas.

Riesgos de Habilidades

¿Tiene su equipo las habilidades para cumplimentar este proyecto? ¿Qué haría si a la única persona que sabe de aspectos de web le ofrecen un trabajo dónde le pagan 10 veces más justo dos semanas antes de que comience el proyecto?

¿Qué recursos existen en su área inmediata para recibir ayuda exterior? Puede necesitarla en distintos momentos de su desarrollo.

Usar un tutor puede colisionar con estos problemas de experiencias. Entrenamientos es otro factor a considerar. Nuevamente, periódicas revisiones del sistema hechos por un reconocido experto son invaluables. Finalmente lea tanto como pueda sobre las nuevas tecnologías y provea a su equipo de abundante material también en esas áreas.

Riesgos políticos

Aunque quiera creerlo o no cada proyecto está cargado de riesgos políticos. Si no tiene experiencia para lidiar con este tipo de problemas, pues tenga pronto a bordo alguien que sepa. Los riesgos políticos tienen un potencial completamente destructivo para el proyecto. Una compañía está llena de gente que tiene sus propias agendas y trabajan muy duro para cumplirlas. Los riesgos políticos se pueden mostrar como simples batallas alrededor del presupuesto en un sabotaje abierto.

Debe saber el entorno político que rodea al proyecto que va a llevar a cabo. Conocer sus amigos y enemigos. Sepa de esta gente de la cual usted puede depender y los que van a luchar contra sus esfuerzos.

Un buen libro para aprender más sobre el manejo de riesgos políticos es “Death March” escrito por Edward Yourdan. El libro es publicado por Prentice-Hall y su ISBN es 0-13-748310-4 (disponible enwww.amazon.com)

Las fases

Ahora vamos a trabajar sobre las fases del ciclo de un desarrollo de proyecto interactivo. Llamará a estas fases: iniciación, elaboración, construcción y transición. Vamos a discutir cada una de estas fases, que serán revisadas como cada punto en el proyecto de desarrollo.

La primera fase de iniciación va a identificar un conjunto de sub-proyectos a construir. Cada uno de estos sub-proyectos va a constar de sus propias fases de elaboración y construcción. Finalmente la fase de transición es donde todos los sub-proyectos se recuperan juntos.

Iniciación

La iniciación es el inicio del proyecto. Esta fase puede manifestarse en una o diferentes formas. Puede abarcar desde una conversación informal tomando un café hasta una reunión bien estructurada con una gran cantidad de personas.

El propósito de esta fase es trabajar en un resumen global del proyecto. Martin Fowler dice: “La iniciación deben ser días de trabajo en los que se debe considerar si vale la pena trabajar durante meses de desarrollo de una mayor investigación durante la elaboración.

El objetivo de la iniciación es tener una buena idea de los casos de negocios para el proyecto. ¿Cuánto puede este proyecto superar la línea principal? Otro aspecto es obtener una idea del alcance del proyecto. ¿Cuánto va a costar este proyecto?

Al final de la fase de iniciación el patrocinador del proyecto está comprometido solo a dar una mirada seria al proyecto.

Dependiendo del tamaño del proyecto puede incluir en si mismo algún grado de análisis con el objetivo de tener la idea del alcance del proyecto en esta etapa. La iniciación puede ser desde una corta conversación hasta un completo análisis de factibilidad que tome muchos meses de trabajo.

Elaboración

Ya cuando está en el punto de comenzar el proyecto, empieza la etapa de elaboración. Este es el punto donde tiene ya una idea muy general de lo que será el proyecto. Quizás usted pueda decir “Vamos a crear una aplicación que va a controlar las actividades de una bolera incluyendo la planificación de las carrileras y las ligas, imprimir los horarios de las ligas los horarios de los trabajadores, guardar la información de la recolección en caja y el control del mantenimiento de los equipos.”

La información requerida puede ser un conjunto de cosas diferente que requerirían mucho texto; pero no nos vamos a detener en este punto.

Las preguntas a responder en este punto son: ¿Qué es realmente lo que va a construir? ¿Cómo lo va a construir? ¿Qué tecnologías estará utilizando para ello?

El mayor enfoque para esta fase debe estar en los riesgos con los que se va a enfrentar. ¿Cuáles son las cosas que pueden descarrilar su proyecto y cómo debe manipularlas? Debe identificar estos riesgos en dependencia de cuánto hay en ellos de problema potencial. El mayor riesgo debe tener la mayor atención.
Estos riesgos deben ser catalogados en los grupos descritos en la sección anterior. Hay que encontrar los riesgos que necesita para comenzar a hacer el análisis y diseño detallado un sistema completo.

En primer punto para comenzar es el modelo de dominio del sistema. Una vez que tenga el modelo de dominio, es necesario moverse entre los casos de uso del sistema y finalmente combinar el modelo de dominio y los casos de estudio en un modelo de diseño.

El Modelo de dominio

El modelo de dominio es un esquema bastante general de cómo opera el negocio. Este modelo describe el mundo en el cual este sistema existirá. Necesitamos una imagen conceptual del negocio de conjunto, como accionar con el, qué cosas haces, cómo hace esas cosas y como encajan todas juntas. El modelo de dominio nos mostrará esto.

El modelo de dominio contiene una mínima cantidad de detalles. Pueden ser diagramas desconectados y estos diagramas pueden ser combinados con libertad con notas y comentarios. El modelo de dominio va a ser la base para un modelo más detallado. La figura1 es un ejemplo de modelo de dominio para un sistema ficticio.


Figura 1 – El modelo de dominio para un sistema de administración de una bolera

Note en la figura 1, no existen detalles dentro de los objetos identificados. Estos objetos son simplemente llamados partes y sus relaciones entre ellos son descritas. El modelo nos muestra una visión general del negocio y cómo funciona.

El éxito de este enfoque es que vamos a lograr una gran imagen del sistema. Para tener la concepción correcta del paquete total, desde el comienzo podemos planificar el impacto de cada pieza del sistema en las otras piezas.

Cuando el modelo de dominio está creado podemos proceder a la identificación de los casos de uso.

Casos de Uso

Martin Fowler describe los casos de uso como “Una interacción típica que el usuario tiene con el sistema para alcanzar resultados. Los casos de estudios proporcionan muchos beneficios al desarrollador. Los casos de estudio son interacciones que el usuario tiene con el sistema, así como es fácilmente comprensible por los usuarios y además provee de una retroalimentación efectiva para este grupo. Los casos de estudio son una especificación funcional. Ya que describen las cosas como se hace de la perspectiva del usuario. Casos de uso para un procesador de texto pueden ser: “hacer texto en negrita”, “hacer texto en cursiva”, “copiar un texto de un lado a otro”, o “crear un índice a un documento”. Estos ejemplos muestran que un caso de uso puede ser una pequeña acción o puede abarcar un proceso largo y complejo.

Ejemplos de casos de uso para nuestra bolera pudieran ser: “El usuario necesita ser capaz de planificar las carrileras para las ligas. Puede existir solo una liga para cada carretera en un momento dado. El espacio de tiempo para cada liga es de 2 horas. Cada liga va a tener la cantidad de carrileras que requiera para un conjunto de juegos de ligas, y “El usuario necesita imprimir la planificación para cada liga” En contra de lo que usted piensa el rango de complejidad para estos casos de uso varían mucho. El texto para el caso de uso debe ser suficientemente específico para los usuarios para comprender la idea del caso y para los desarrolladores a tener una idea de que afecta la implementación y la funcionalidad.

Los casos de uso han sido dibujados como se ve en la figura 2


Figura 2 – Un diagrama de caso de uso.

En la figura 2 puede ver que los casos de uso se representan por un óvalo con el nombre del caso de uso dentro. La “figurita” es llamada el actor y representa la persona o cosa que usa la funcionalidad. El actor puede ser un usuario o puede ser otro sistema de computadoras o incluso una máquina en la empresa.

El diagrama de caso de uso le permite ver una idea general de cómo encajan juntos.

Una vez que el modelo de dominio se ha creado y se han identificado los casos de uso se puede proceder con el modelo de diseño.

El Modelo de Diseño

El modelo de diseño identifica los objetos que el sistema contendrá y los casos de estudios las actividades que el sistema va a automatizar. El modelo de diseño es la combinación de estos dos aspectos. El modelo de diseño es también un modelo abstracto en el que no se incluye un alto nivel de detalle. Los diagramas detallados se crearán más tarde en el ciclo de desarrollo.

El propósito de este modelo de diseño es describir la combinación de la información en el modelo de dominio y el comportamiento de los casos de uso en el estilo que nos muestra cómo estas cosas encajan juntas. El modelo de diseño también provee una arquitectura reutilizable que permite para futuras extensiones del sistema.

La figura 3 es un ejemplo de un modelo de diseño para nuestro sistema de la bolera. El modelo de la figura 3 está muy lejos de ser completo, tiene solo la intención de demostrar la idea de la combinación del modelo de dominio con los casos de uso


Fig3 – Modelo de diseño

Note que la figure 3 añade el comportamiento de los casos de uso a los objetos del modelo de dominios. Los diagramas serán expandidos en el actual diagrama en un momento posterior. Aquí estamos tratando de tener una visión más completa del sistema entero.

¿Cuán lejos iremos con los diagramas?

Cuando leemos acerca de análisis y diseño de sistemas estamos viendo constantemente diferentes diagramas. Un diagrama para esto y diagramas para lo otro. ¿Cuántos diagramas son suficientes y cuántos son demasiado? ¿Qué debemos usar además de estos diagramas para la comunicación?

Los diagramas pueden ser usados cuando aportan al entendimiento del sistema y deben ser evitados cuando causan confusión. Ward Cunningham dijo: “Seleccionar cuidadosamente los memos escritos puede fácilmente sustituirse por la documentación de diseño comprensible y tradicional. Excepto en puntos aislados. Eleve esos puntos y olvídese de lo demás”

¿Qué significa esta cita? ¿Significa que debemos o no dibujar los diagramas? No, significa que los diagramas deben ser hechos cuando ayuden al entendimiento de los sistemas. Si hay diagramas que no enriquecen el entendimiento, entonces debemos olvidarnos de ellos. Muchos de los memos cortos bien escritos pueden mejor y más claramente describir nuestro punto que un complejo y confuso diagrama. En estas situaciones deje el diagrama y escriba el memo.

¿Cuándo está completa la fase de elaboración?

La fase de elaboración de un proyecto está completa cuando todos los riesgos relacionados con el proyecto están bien definidos y existen los planes para manipularlos. Además todas las tecnologías han sido identificadas y los modelos existentes para el dominio, caso de uso y diseño. Además, los desarrolladores están provistos de los estimados para la creación de cada caso de uso.

Cada caso de uso se convertirá en uno de los sub-proyectos en la fase de construcción. Esto es donde la iteración juega su papel. Durante la construcción vamos a crear cada caso de uso por separado y permitir que la experiencia de crear cada uno influya en el diseño en las otras.

Construcción

Una vez que la elaboración está completa entramos en la fase de construcción. En la construcción de cada caso de uso, los manipulamos como un proyecto ya que será construido a través del análisis, diseño, codificación, pruebas y proceso de iteración. Una iteración termina con un demo a los usuarios del subsistema completado.

Durante la construcción de un caso de uso, frecuentemente vamos a descubrir cambios que van a repercutir en nuestro diseño preliminar para otros casos de uso. Estos descubrimientos van a retroalimentar los diseños de casos de uso. También vamos a descubrir nuevos casos de uso que fueron realizados durante la fase de elaboración. Estos pueden adicionarse al proyecto y ser planificados para una construcción posterior.

Cuando completamos la construcción de cada caso de uso vamos a integrarlos con la construcción previa de casos de uso para trabajar a través de sistemas completamente integrados. Podemos reingresar la construcción de los casos de uso previamente completados y las necesidades originadas. Este proceso que da el enfoque del nombre de desarrollo iterativo, como un sistema completo en la construcción del cual hay una serie de iteraciones en cada subsistema.

Transición

La transición es la fase final del enfoque de desarrollo iterativo. La transición manipula esos aspectos que no fueron referenciados durante la construcción. Es posible que no haya alguna integración final a hacer después que todos los subsistemas hayan sido creados. Un buen ejemplo del problema que puede ser referenciado durante la transición es la optimización del rendimiento.

La optimización usualmente sacrifica claramente y facilita la integración a favor del mejoramiento del rendimiento. Esto no es siempre así, ya que nosotros queremos hacer pronto en el proyecto de desarrollo tanto como que el va a incrementar las dificultades en la creación del proyecto. En su lugar dejaremos la optimización para la fase de transición cuando todo el subsistema esté creado y probado.

La optimización es además un logro fugaz. Frecuentemente, nosotros, como desarrolladores, percibimos un problema de rendimiento cuando los usuarios no lo notarían nunca. Nosotros vamos también a sobre ver el problema del rendimiento cuando los usuarios difícilmente lo reconozcan. Si empezamos a optimizar el sistema antes que los usuarios puedan decir dónde son los puntos que ven lentos, gastaremos mucho tiempo y esfuerzo en lugares erróneos.

La transición puede ser pensada como el período de tiempo entre liberar una versión beta y la versión final del proyecto. Será como un bug fijo, en los enganches funcionales. Optimización de rendimiento, y otras cosas hechas durante esta fase. A veces podemos descubrir un caso de uso enteramente nuevo que necesitamos para no ser creado. El enfoque de desarrollo iterativo nos permite facilitar el proceso de este nuevo caso de uso y luego re-entrar en la fase de transición.

Resumen

El análisis y diseño orientado a objetos es frecuentemente visto como un enorme paquete de complejos diagramas que pueden significar poco o nada para los usuarios del sistema. Es frecuentemente visto como un proceso que envuelve una gran cantidad de conceptos abstractos y teóricos modelos que realmente aportan muy poco al proyecto.

No tiene que ser de esta forma. El análisis y diseño orientado a objetos puede ser un enfoque muy pragmático del desarrollo de un sistema. No tienen que ser frases distintas que nunca sean revisadas. El uso de un enfoque iterativo, ya que este proceso puede ser un aspecto que vive y respira en el desarrollo del sistema. Puede enriquecer nuestra habilidad de encontrar la necesidad de los usuarios y el control de los riesgos asociados con el proyecto. Cuando se usan efectivamente, el enfoque iterativo puede realmente reducir el tiempo requerido para la creación de un proyecto.