Reutilización de código, mantenimiento de aplicaciones (III)
Introducción
Nos encontramos en una encrucijada.
Nuestra aplicación parecía sencilla y empezó siéndolo, pero los requisitos inicialmente marcados han variado y nos está empezando a dar algún que otro dolor de cabeza.
No obstante, hemos sabido adaptarnos a esos requisitos y hemos modificado nuestra aplicación para cubrirlos.
Implementación de la solución
La idea inicial es la de partir del código de la entrada anterior, refactorizar o acondicionar de forma general el código para que cumpla los requisitos de forma aceptable y posibilite un mejor mantenimiento del código.
Basándonos aún en una aplicación Windows, nuestro código quedará de la siguiente forma.
En primer lugar, la lista enumerada:
1: namespace UI_Sample
2: {
3:
4: public enum ConnectorType
5: {
6: Excel,
7: Text
8: } // ConnectorType
9:
10: } // UI_Sample
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, «Courier New», courier, monospace;
background-color: #ffffff;
/white-space: pre;/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Como podemos ver en el código anterior, nuestra clase enumerada tiene dos valores, uno para definir los ficheros de tipo Excel y otro para los de tipo Text o texto.
De esta manera, podremos indicar el origen y destino de forma más flexible.
El código de nuestra aplicación Windows quedará ahora de la siguiente forma:
1: namespace UI_Sample
2: {
3:
4: using System;
5: using System.Collections.Generic;
6: using System.ComponentModel;
7: using System.Drawing;
8: using System.Windows.Forms;
9:
10: public partial class MainForm : Form
11: {
12:
13: public MainForm()
14: {
15: InitializeComponent();
16: // Por defecto es un fichero de texto.
17: this.Connector = ConnectorType.Text;
18: } // MainForm Constructor
19:
20: public ConnectorType Connector { get; set; }
21:
22: private void btnSampleTest_Click(object sender, EventArgs e)
23: {
24: string data = String.Empty;
25: // Leemos la información.
26: data = Read();
27: // Escribimos el resultado del supuesto proceso anterior.
28: Write();
29: // Mostramos un mensaje en pantalla tipo fake.
30: MessageBox.Show(
31: data +
32: Environment.NewLine +
33: "Lectura y escritura realizada.");
34: } // btnSampleTest_Click
35:
36: private string Read()
37: {
38: switch (this.Connector)
39: {
40: case ConnectorType.Excel:
41: // Excel
42: return "Excel leída";
43: // Hacemos algo con la información leída.
44: // ...
45: break;
46: default:
47: // Texto
48: return "Texto leído";
49: // Hacemos algo con el texto leído.
50: // ...
51: break;
52: }
53: return String.Empty;
54: } // Read
55:
56: private void Write()
57: {
58: switch (this.Connector)
59: {
60: case ConnectorType.Excel:
61: // Excel
62: // Escribimos Excel.
63: break;
64: default:
65: // Texto
66: // Escribimos Texto.
67: break;
68: }
69: } // Write
70:
71: } // MainForm
72:
73: } // UI_Sample
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, «Courier New», courier, monospace;
background-color: #ffffff;
/white-space: pre;/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
Hemos preparado una propiedad para indicar el tipo de conector a utilizar.
Y posteriormente hemos llamado a una función Read y a un método Write para llevar a cabo nuestras acciones.
Indudablemente el código tiene un carácter meramente descriptivo porque faltarían más cosas como implementar la lógica de lectura y otras partes importantes de nuestra solución, pero lo importante aquí es quedarnos con la idea.
Posibles problemas
Empezamos con una aplicación para procesar archivos de texto.
Más tarde, los requisitos cambiaron y nos pedían procesar archivos de texto y Excel. Hicimos los cambios, pero nos dimos cuenta que de cara al mantenimiento, podríamos tener algún que otro quebradero de cabeza, así que hemos pensado en refactorizar un poco el código y mejorar algunas cosas.
Así surge esta tercera revisión que mejora aspectos relacionados con el mantenimiento. Además, si alguien nos pide ahora agregar otro conector como por ejemplo uno para leer y escribir en base de datos, bastará con agregar un nuevo elemento a nuestra lista enumerada y modificar Read y Write para que procesen también entradas y salidas con bases de datos.
Parece que todo está más o menos acorde con lo que buscamos, pero tenemos delante de nosotros varios problemas bastante más graves que a simple vista muchas veces no vemos.
Por un lado la separación de responsabilidades. Aquí el lema es divide y vencerás. Si queremos hacer algo que nos va a llevar mucho tiempo o proceso, lo mejor es separar en unidades pequeñas cada una de esas actividades. Pensemos por poner un ejemplo, en una línea de montaje.
Por otro lado, y en el caso de que hiciéramos pruebas unitarias, la posibilidad de probar las porciones de código por separado y centrando los esfuerzos y dedicación únicamente en aquellas unidades de Software que realmente cambian sin que estas afecten a un todo. Pensemos en que alguna parte de nuestro Software se modifica, como por ejemplo que aparece una nueva versión de Excel que afecta como es lógico al proceso de documentos Excel. Si el resto de código de nuestra aplicación funciona perfectamente, la forma en la que hemos codificado nuestra aplicación nos obliga lo queramos o no, a tocar una parte de código que entremezcla cosas que funcionan y otras que no. Además de que puede haber diferentes personas tocando el mismo código y que existen herramientas que lo permiten, es cierto que es un foco de problemas, y además, es posible que toquemos accidentalmente código que funcionaba, por lo que podemos crear nuevos problemas.
Otro aspecto a destacar y no menos importante, tiene que ver con la dependencia. Y es que nuestra aplicación depende fuertemente de unas piezas de Software que entremezclan UI con lógica de negocio y no sólo ensucian nuestro desarrollo, sino que enmaraña el mantenimiento de la misma y dificulta su depuración.
Y todo esto sin poner encima de la mesa otro de los problemas más habituales y que siempre pensamos en ellos al final, la reutilización del código realizado. En este caso, al estar todo integrado dentro de una misma aplicación como es la interfaz de usuario, su reutilización se nos antoja al menos, compleja.
¿Qué hacer entonces?. ¿Cómo resolver estos problemas?.
En la próxima entrada veremos como resolver alguno de estos problemas.