26 de febrero de 2010

MSF for Agile y XP

Recientemente Alcides me mandó un correo en donde una persona pregunta sobre las similitudes y diferencias entre MSF for Agile y XP, y si es posible usarlos juntos o usar XP con la suite de herramientas de Microsoft.

Antes de entrar en tema me parece correcto aclarar que MSF for Agile no es una metodología, ni un proceso de desarrollo--como si lo es XP. MSF for Agile es lo que Microsoft denomina un “marco de soluciones”. Es decir, se limita a proporcionar a sus usuarios con algunos artefactos, modelos, conceptos y guías.

En particular, si se observa el MSF for Agile Software Development Process Template y el MSF for Agile Software Development Process Guidance, uno puede ver de inmediato que están pensados para usarse con Scrum. Aunque se supone que MSF es agnóstico en cuanto a metodologías se refiere, existe una muy fuerte influencia de RUP y de Scrum.

Ahora bien, desde ahí existe una diferencia sin necesidad de entrar en más detalle. Hablando llanamente, Scrum y XP fueron pensadas y diseñadas desde dos puntos de vista diferentes:

  • Scrum está pensado como un proceso ágil de administración de proyectos, mientras que
  • XP está pensado como un proceso ágil de desarrollo de software.

De hecho, en cierta forma se puede pensar en ellos como procesos ortogonales:

  • Scrum se ocupa primordialmente de los aspectos de gestión y comunicación del proyecto e intencionalmente deja fuera los aspectos referentes a las prácticas técnicas específicas usadas durante el proyecto.
  • XP por otra parte, se concentra en los aspectos más técnicos de la ejecución del desarrollo. Aunque XP contiene prácticas orientadas a la planeación y administración del proyecto, estas tradicionalmente han sido poco explicadas y por ende, poco comprendidas.

Un dato relevante al respecto es que de hecho, Ken Schwaber recomienda el uso de las prácticas de ingeniería de XP (ver figura) cuando se desarrolla un proyecto de software usando Scrum.

circles

Así, mientras que Scrum puede ser usado para gestionar cualquier clase de proyecto, XP es específicamente para el desarrollo de software. Es por ello que Scrum es tan aludido recientemente: Es más fácil de entender para los gerentes y demás personas de perfil no-necesariamente-técnico.

En cuanto a su relación con MSF, éste principalmente se dedica a promover algunas prácticas específicas para el uso con productos de Microsoft durante la mayor parte del ciclo de vida del desarrollo. Provee de guías, plantillas de documentos, sitios web, y demás cosas para aplicarlas durante el proyecto.

¿No tienes un formato oficial para casos de uso? MSF for Agile Software Development Process Template trae plantillas en formato Word.

¿Necesitas realizar un documento de Backlog para tu proyecto/sprint? MSF for Agile Software Development Process Template trae plantillas de Excel para ello.

¿Necesitas un sitio de colaboración para tu equipo de trabajo? MSF for Agile Software Development Process Template contiene una plantilla de sitio Web para Sharepoint, incluyendo blog, wiki y otras monerías.

Además, Microsoft Team System incluye software para control de versiones, seguimiento de pendientes, integración continua, reportes, etc.

Es verdad que Microsoft ha incluido varias características en sus productos que pueden resultar atractivas a los equipos de desarrollo ágiles, pero en ningún momento se puede decir que se trate de productos para el desarrollo ágil.

Un proyecto ágil puede llevarse a cabo igualmente usando herramientas Open Source para las mismas tareas:

Y un largo, largo etcétera.

Microsoft ha integrado documentación, plantillas, artefactos y herramientas de desarrollo en un paquete que espera que sea lo suficientemente atractivo para que los desarrolladores decidamos usarlo en vez de alguna otra alternativa.

Por lo tanto, si lo que estamos tratando de resolver es cuál método usar para nuestros proyectos, la pregunta MSF o no-MSF no es siquiera relevante. Primero debemos revisar los principios detrás de los métodos que estamos evaluando y ver qué tanto se adaptan dichos principios a la cultura actual de mi empresa, mi equipo de trabajo (o a lo que quiero que llegue a ser dicha cultura) y a las necesidades de mi proyecto.

Si nos decidimos por usar Scrum o RUP y además estamos usando o planeamos usar la suite de herramientas de Microsoft, entonces es conveniente darle un vistazo a MSF, siempre recordando que este no es un fin en si mismo, sino una herramienta más que habrá de usarse y adaptarse a mis necesidades para resolver el problema que tengo entre manos.

Saludos.

24 de febrero de 2010

¡¡Polimorfismo, polimorfismo, polimorfismo!!

Quizás la característica más importante de los entornos orientados a objetos sea el poco comprendido y aún menos usado polimorfismo.

No me malinterpreten: la encapsulación es importante, pero ya COBOL y sus módulos podían esconder los datos mediante interfaces "bien definidas".

En un lenguaje orientado a objetos, la encapsulación no es sino aplicar los principios de modularidad hasta sus últimas consecuencias

Lo que es más, es probable —y muchas veces aún deseable— romper la encapsulación de un objeto cuando su único propósito es el trasporte de datos. Esto es tan común que incluso hay un patrón que lo documenta: DTO (Data Transfer Objects)

Sin embargo, el tema que quiero tocar el día de hoy es uno que consistentemente encuentro que los sistemas legacy pasan por alto: el polimorfismo.

Como una definición simple diremos que el polimorfismo es la capacidad para mandar un mismo mensaje a varios objetos de —potencialmente— distintos tipos y que estos procesen dicho mensaje de la forma más adecuada posible.

Por ejemplo, en Java o .NET podemos llamar al método ToString de cualquier objeto sin importar su tipo, y obtener una respuesta adecuada. Así pues, si el objeto es el Int32 13, obtendremos la cadena "13"; si el objeto es un registro de un sistema de crédito, tal vez obtengamos una cadena con el nombre del acreditado y su saldo al corte; si el objeto es un String, entonces obtendremos el mismo objeto como respuesta.

Como ven, cada objeto responde de manera distinta a la invocación de ToString, pero siempre de manera consistente con:

  1. Lo que se espera de dicho método (una representación de cadena del objeto en cuestión), y
  2. La naturaleza del objeto mismo (sus reglas de negocio, etc.)

Si el polimorfismo es tan maravilloso, ¿Porqué nadie lo usa?

Aquí quisiera hacer una aclaración. Como lo demuestra el ejemplo anterior, en realidad usamos el polimorfismo todo el tiempo. Sin embargo, como desarrolladores no hemos asimilado el pensamiento en objetos y seguimos anclados a las mismas técnicas y los mismos paradigmas de antaño. Nadie extraña lo que no ha tenido. Si nunca hemos aprendido a utilizar correctamente una herramienta y a dominarla, seguiremos naturalmente a la tendencia de usar aquellas que mejor conocemos y que nos han funcionado en el pasado.

Ahora, no quiero aquí discutir si las técnicas tradicionales funcionan o no. Es claro que funcionan puesto que se siguen usando. El punto es qué tan adecuadas son para la tarea que les hemos asignado.

Como comenté al principio del blog, algo que consistentemente veo en sistemas legacy (¡¡incluso algunos que se desarrollaron hace 2 meses!!) es la completa falta de polimorfismo en su diseño e implementación. ¿Cual es la alternativa escogida en su lugar? Series interminables de IFs y SELECT CASE/SWITCH regadas prácticamente por la totalidad de la aplicación. Algo parecido a esto:

public Money GetSaldo() {
  ...
  if (Cuenta.Tipo == C_CORRIENTE) {
    Interes = [calcular el interés de cuentas corrientes]
  } else if (Cuenta.Tipo == C_CHEQUES) {
    Interes = [calcular el interés de cuentas de cheques]
  } else {
    Interes = [calcular el interés de cuentas "comodity"]
  } 

  Saldo += Interes;

Al final, vemos listas interminables de condiciones que "checan" el tipo de cuenta para la aplicación de muy diversas reglas de negocio, diseminadas por todo el código sin un lugar bien definido para su definición y su aplicación.

Lo que un código como el anterior nos está diciendo prácticamente a gritos es que existen (por lo menos) tres tipos de cuentas y cada uno tiene comportamientos bien diferentes en contextos específicos.

Algunos podrán pensar ¡Ah! entonces hay que heredar tres subclases de Cuenta. Pues no, o por lo menos no necesariamente. Efectivamente, en lenguajes que la soportan, podemos usar herencia como nuestro vehículo para crear objetos polimórficos, sin embargo, algo que en su momento me costó trabajo comprender (probablemente por mis raizes de lenguajes fuertemente tipados) es que el polimorfismo puede operar de forma independiente a la herencia.

Si estamos trabajando en Java o .NET, es posible utilizar una interface. En C++ y Visual Basic 5 o 6, usamos una clase abstracta, es decir, una que solo contiene métodos virtuales sin código. En Smalltalk, Ruby, Python, Javascript, Objective-C y —sorpresa, sorpresa— de nuevo, Visual Basic 5, 6 o VBscript, etc., podemos enviar mensaje y dejar que el sistema de mensajería del lenguaje resuelva la invocación del método apropiado.

Regresando al ejemplo anterior, suponiendo que tenemos tres clases que implementan el cálculo del interés de sendos tipos de cuenta y que responden adecuadamente al mensaje GetInteres, podemos reescribirlo de la siguiente forma:

interface Rendimientos {
  Money GetInteres(Cuenta c);
}

class CuentaCorriente : Rendimientos {
  public Money GetInteres(Cuenta c) {
    return [calcular el interés de cuentas corrientes];
  }

class CuentaCheques : Rendimientos {
  public Money GetInteres(Cuenta c) {
    return [calcular el interés de cuentas de cheques];
  }

class CuentaSimple : Rendimientos {
  public Money GetInteres(Cuenta c) {
    return [calcular el interés de cuentas "comodity"];

public Money GetSaldo() {
  ...
  Saldo += rendimiento.GetInteres(this);

En este caso he optado por definir una interfaz llamada Rendimientos. Nótese que no he usado la nomenclatura usual agregando una "I" al principio, ya que si más adelante decido que hay código en común en las clases que la implementan, mismo que me gustaría consolidar, puedo convertir a Rendimientos en una clase abstracta sin demasiados problemas y sin preocuparme de tener ahora una nomenclatura inconsistente por un lado, o tener que cambiar todas las menciones de la misma por el otro.

Analicemos las ventajas y desventajas que tiene el enfoque alternativo:

  • Ventajas
    1. El código que hace uso del método polimórfico se simplifica considerablemente.
    2. Las clases y métodos resultantes de la descomposición son altamente cohesivos, virtud de lo cual
    3. Existe un lugar claro en el código para la definición de reglas de negocio relacionadas con las variantes identificadas.
    4. El código de los métodos es sencillo y por lo tanto más fácil de comprender.
    5. El código es más sencillo de probar, ya que solo maneja una variante a la vez.
  • Desventajas
    1. Es necesario crear un mayor número de clases.
    2. No se puede saber con solo observar el código cual método se ejecutará, ya que esto se resuelve en tiempo de ejecución.
    3. Requiere de un diseño más concienzudo que la inclusión ad hoc de sentencias IF "según se vaya ocupando".

Vuelvo a hacer énfasis en algo que he comentado varias veces. Algunas personas opinan que aparentemente, el uso de la programación orientada a objetos aumenta la complejidad del código. Esto es una falacia. Hay que distinguir entre "código simple" y "código simplón":

Código simple
Es código que expresa su intención de forma clara, aún si no se conocen todos los detalles que lo hacen funcionar.
Código simplón
Es código escrito con las mismas cinco instrucciones que aprendí en mi curso de Introducción a la Programación, sin importar cuan rebuscado pueda ser el resultado final.

Nadie se lo piensa dos veces al llamar ToString el su código o al concatenar una cadena con un número (usando implícitamente ToString). ¿Porqué entonces el crear mi propio código polimórfico es más complejo?

Teclear no es un sustituto para pensar.

Al descomponer esas largas y complejas rutinas en sub-rutinas, estamos aplicando "divide y vencerás". Al mover esas sub-rutinas a clases polimórficas, aplicamos "a cada quien lo suyo".

Con el tiempo, en lugar de tener unas pocas clases grandes y complejas, terminaremos con un montón de clases pequeñas y simples. De eso se trata la programación y muy en especial la programación orientada a objetos: De manejar la complejidad inherente de los sistemas. De ocultarla si es posible, no de incrementarla con programación descuidada y por debajo de lo que los usuarios esperan de nosotros.

Saludos.