miércoles, 15 de agosto de 2007

Meaningful

Según el significado encontrado en google esta palabra significa: having a meaning or purpose; "a meaningful explanation"; "a meaningful discussion"; "a meaningful pause"

En español podríamos traducir como: Que tiene un significado o propósito. Esto nos da una mejor idea del significado de esta palabra que dentro del desarrollo ágil el desarrollo guiado por pruebas de sus siglas en inglés TDD (Test Driven Development), acuña el termino Meaningful o Significativo para el nombramiento de los métodos (ver pagina 42 del libro Test-Driven Development in Microsoft .NET de James W. Newkirk y Alexei A. Vorontsov).

Veamos literalmente el comentario: "Meaningful method names are important for code readability and in turn its overall maintainnability. In short, method names should convey their intentions." Que parafraseado diriamos lo siguiente: los nombres de los metodos deben de comunicar un significado o su propósito para la legilibidad del codigo y mantenimiento. En resumen los nombres de los métodos deberían transmitir sus intenciones.

Y para ser justos haremos una referencia al libro de Refactoring de Martin Fowler que acuño el concepto de Refactorización del código y que luego surgió el concepto de desarrollo ágil. De hecho en el libro de TDD la referencia de la página hace mención de la refactorización como una disciplina importante a tomar en cuenta en el desarrollo guiado por pruebas o TDD.

En la página 273 del libro de Martin Fowler "Refactoring". Una técnica de la refactorización es el renombrar los métodos y explica: La Motivación es los métodos deberían nombrarse en un forma que comunica su intención. Recuerde su código es para humanos primero y luego para la computadora. Termina diciendo los buenos nombres, es una habilidad que requiere práctica: mejorar esta habilidad es la llave para empezar ha ser un verdadero programador hábil.

Y para finalizar esta introducción al termino Meaningful vamos acuñar el concepto de la disciplina llamada "Refactoring", no es mas que: El proceso de cambiar un sistema de software de tal manera que este no altera el comportamiento externo del código pero mejora su estructura interna (Pagina XVI del libro en mención).

Esta breve introducción da pie al siguiente ejemplo:

Escenario:

Es una aplicación Web y tenemos un control de usuario que hace la búsqueda de proveedores por tres filtros que serán controles de texto o Textbox identificados de la siguiente manera: NITProveedor, NombreProveedor y CodigoInternoProveedor. Deseamos darle el comportamiento de tal forma que si el usuario ingresa un valor en alguno de los controles de entrada los otros controles nos aseguremos que es eliminado cualquier valor de los demás controles para que al buscar por cualquiera de ellos y no por la combinación de varios de ellos nos traiga la información requerida por el usuario.

Para ir directo al grano y delimitar de alguna forma el alcance de este ejemplo no nos detendremos a redactar la parte de búsqueda de la información requerida por el usuario, sino únicamente el comportamiento que deseamos que tenga los controles para mostrar un ejemplo del concepto Meaningful que es la motivación de una de las técnicas de Refactoring de renombrar métodos. El ejemplo esta hecho en Visual Studio 2005 y en C# 2.0 para los que preguntan.

Veamos por una mera referencia o idea visual el código html de la página en la cual vamos a trabajar:

<%@ Page Language="C#" AutoEventWireup="true" Codebehind="TeachingTheConceptMeaningFul.aspx.cs"

Inherits ="TeachingTheConceptMeaningFul" %>

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

< html xmlns="http://www.w3.org/1999/xhtml">

< head runat="server">

<title>Busqueda de Proveedores</title >

</ head >

< body >

<form id="form1" runat ="server">

<div>

Proveedor:&nbsp;

<asp:TextBox ID ="NITProveedor" runat="server" OnTextChanged="NITProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el NIT del proveedor."/>

&nbsp;

<asp:TextBox ID ="NombreProveedor" runat="server" OnTextChanged="NombreProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el nombre del proveedor."/>

&nbsp;

<asp:TextBox ID ="CodigoInternoProveedor" runat="server" OnTextChanged="CodigoInternoProveedor_Cambio"

AutoPostBack ="true" ToolTip="Ingrese el codigo interno del proveedor."/>

</ div>

</form>

</ body>

</ html>

Veamos la parte remarcada donde esta el caso de nuestro ejemplo, Tenemos los tres controles y cada uno de ellos tiene un evento que se ejecuta cada vez que cambia el valor ingresado en cada uno de los controles de texto la propiedad AutoPostBack nos indica que hará un postbak al servidor cada vez que cambie el texto al perder el foco del control, esto sucede asi por el ambiente Web en que estamos trabajando y la orientación de las aplicaciones asp.net que fueron diseñadas para validar del lado del servidor preferiblemente y no del lado del cliente, aunque se puede. Y por ultimo el útil Tooltip que aparece un recuadro en amarillo que aparece al acercar el cursor sobre el control en el cual estamos navegando.

Ahora veamos el código del lado del servidor que es el que realmente nos interesa.

public partial class TeachingTheConceptMeaningFul : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

}

protected void NITProveedor_Cambio(object sender, EventArgs e)

{

this.NombreProveedor.Text = null;

this.CodigoInternoProveedor.Text = null;

}

protected void NombreProveedor_Cambio(object sender, EventArgs e)

{

this.NITProveedor.Text = null;

this.CodigoInternoProveedor.Text = null;

}

protected void CodigoInternoProveedor_Cambio(object sender, EventArgs e)

{

this.NITProveedor.Text = null;

this.NombreProveedor.Text = null;

}

}

Al apreciar este código parece que todo esta muy claro y que no es difícil que comprender, pero que pasa si tenemos que agregar otros filtros digamos unos 2 o 3 o tal vez 5 filtros mas deberemos repetir el código y tener el cuidado de no limpiar el contenido del control en el que el usuario. Pero ver asignar null al contenido de cada control de texto verdaderamente comunica el propósito de lo que estamos haciendo?. Bueno no me conteste ahora apliquemos la refactorización a nuestro código para hacerlo mas legible o por lo menos para centralizar las mismas operaciones.

Digamos que vamos a tener un solo método que se conecta a todos los controles y desde allí centralizamos el comportamiento de cada control.

Para ello comentamos el código html original para regresar al código anterior si todo nos sale mal (esto es una buena practica no eliminar el código hasta estar seguro que el cambio quedo bien. Y luego indicamos que el método al cual se pegará el disparar el evento del cambio del texto de cada control es el mismo y lo llamaremos "FiltroProveedor_Cambio". Veamos el código:

< div >

Proveedor:&nbsp;

<asp :TextBox ID="NITProveedor" runat="server" OnTextChanged ="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el NIT del proveedor." />

&nbsp;

<asp :TextBox ID="NombreProveedor" runat="server" OnTextChanged ="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el nombre del proveedor." />

&nbsp;

<asp:TextBox ID ="CodigoInternoProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack ="true" ToolTip="Ingrese el codigo interno del proveedor." />

</ div>

Veamos ahora el código del lado del servidor:

protected void FiltroProveedor_Cambio(object sender, EventArgs e)

{

TextBox ControlTexto = (TextBox)sender;

switch (ControlTexto.ID)

{

case "NITProveedor":

this.NombreProveedor.Text = null;

this.CodigoInternoProveedor.Text = null;

break;

case "NombreProveedor":

this.NITProveedor.Text = null;

this.CodigoInternoProveedor.Text = null;

break;

case "CodigoInternoProveedor":

this.NITProveedor.Text = null;

this.NombreProveedor.Text = null;

break;

default :

break;

}

}

Que hicimos ahora, no mucho solo centralizamos el problema, no nos comunica mucho, es prácticamente el mismo código pero así es en la refactorización empezamos con pequeños cambios, para prepararnos para mejorar nuestro código. Es importante mencionar que aunque es valido utilizar el switch como una estructura de selección es muy rígida y que dentro de los patrones de diseño (Gof Design Patterns ) sugiere evitar su uso lo mas posible, con el objetivo que nuestro código sea mas adaptable y extensible con el menor cambio posible. Aplicando este concepto e implementando una solución posible y sencilla modificaremos nuestro código para hacerlo extensible y adaptable a los futuros cambios sin alterar nuestro código luego de estos cambios:

Primero vamos a convertir el div que agrupa los controles como un control de servidor y lo utilizaremos como un contenedor de controles para utilizar un loop para recorrer los controles dentro del div. Así como aparece a continuación:

<div id="ContenedorFiltrosProveedor" runat ="server">

Proveedor:&nbsp;

<asp:TextBox ID ="NITProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el NIT del proveedor." />

&nbsp;

<asp:TextBox ID ="NombreProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el nombre del proveedor." />

&nbsp;

<asp:TextBox ID="CodigoInternoProveedor" runat="server" OnTextChanged ="FiltroProveedor_Cambio"

AutoPostBack ="true" ToolTip="Ingrese el codigo interno del proveedor." />

</ div>

Ahora veamos el código del lado del servidor:

protected void FiltroProveedor_Cambio(object sender, EventArgs e)

{

TextBox ControlTexto = (TextBox)sender;

foreach (Control control in this.ContenedorFiltrosProveedor.Controls)

{

TextBox texto = control as TextBox;

if (texto != null)

{

if (texto.ID != ControlTexto.ID)

texto.Text = null;

}

}

}

Esto cambio completamente la rigidez de la estructura switch y nos permitirá agregar otros filtros tipo TextBox sin alterar el código. Veamos un ejemplo, digamos que vamos agregar un nuevo filtro tipo TextBox veamos lo que necesitamos hacer en el código html:

<div id="ContenedorFiltrosProveedor" runat ="server">

Proveedor:&nbsp;

<asp:TextBox ID ="NITProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el NIT del proveedor." />

&nbsp;

<asp:TextBox ID ="NombreProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el nombre del proveedor." />

&nbsp;

<asp:TextBox ID ="CodigoInternoProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack ="true" ToolTip="Ingrese el codigo interno del proveedor." />

<asp:TextBox ID="RazonSocialProveedor" runat="server" OnTextChanged="FiltroProveedor_Cambio"

AutoPostBack="true" ToolTip="Ingrese el nombre de la razón social del proveedor." />

</ div>

Y del lado del servidor, absolutamente nada. Pero un minuto no hemos terminado ser recuerdo el objetivo del ejemplo es aprender a darle un nombre a los métodos que expliquen su propósito o significado, para ello vamos a utilizar la opción de Visual Studio 2005 Refactor (click derecho sobre el código seleccionado( y Seleccionaremos la opción ExtratMethod y ahora si colocaremos el nombre del método, como aparece a continuación:

protected void FiltroProveedor_Cambio(object sender, EventArgs e)

{

TextBox ControlTexto = (TextBox)sender;

LimpiarValorControlesTexto(ControlTexto);

}

private void LimpiarValorControlesTexto(TextBox ControlTexto)

{

foreach (Control control in this.ContenedorFiltrosProveedor.Controls)

{

TextBox texto = control as TextBox;

if (texto != null)

{

if (texto.ID != ControlTexto.ID)

texto.Text = null;

}

}

}

De alguna manera encapsulamos la funcionalidad y explicamos claramente lo que hace nuestro codigo sin decirlo como lo hace ya que esto no es el objetivo de la encapsulación y si del Concepto Meaningful Method Name.

Hasta la próxima. Write Code For Fun!,

Manolo