|
Programando aplicaciones nos podemos encontrar con el caso de que una operación costosa en cuanto al tiempo de ejecución o al consumo de recursos en general (acceso a disco, red, etc...) se invoca varias veces, con el consecuente trabajo y consumo de recursos que conlleve, y sin embargo, es posible que los resultados nos valgan de una invocación para otra.
Si es ese el caso, podemos utilizar la técnica conocida como "bits de suciedad". Aunque se puede aplicar con programación imperativa, le sacaremos el máximo provecho en POO.
El nombre de bit de suciedad (dirty bit) se utiliza también en otros contextos. Por ejemplo, en un buffer o una memoria, se asocia un "bit de suciedad" para todos aquellos bloques de la memoria principal que difieren de su copia en memoria secundaria. En nuestro caso, lo que haremos será asociar un bit a cada operación costosa (en tiempo u otros recursos) de nuestra clase que sepamos que es susceptible de ser solicitada más de una vez. Para representar a ese bit, utilizaremos un simple booleano. Ese bit nos debe indicar si la operación debe realizarse o si nos vale el resultado de la última vez que lo ejecutamos. Si el bit de suciedad vale falso, significará que el resultado está "limpio", es decir, no necesita ser recalculado. Si el bit de suciedad vale verdadero, significará que el resultado está "sucio", es decir, necesita ser recalculado. Vamos a construir una clase de ejemplo para ver esto. En este caso, nuestra clase representará a una simple lista de enteros, y le vamos a poner dos operaciones: una para añadir un nuevo entero al final de la lista, y otra para obtener el máximo de todos los enteros almacenados. Calcular ese máximo es una operación costosa. Asociaremos un bit de suciedad (un booleano) a esa operación, y reservaremos una variable de instancia privada que represente al resultado de esa operación. Incialmente, pondremos el bit de suciedad a verdadero, es decir, el resultado no es válido. En la operación de calcular el máximo, antes de empezar comprobaremos si el bit de suciedad es verdadero, y sólo en ese caso realizaremos la operación. Al terminar, pondremos el bit de suciedad a falso, indicando que el resultado es válido. Cada vez que se añada un elemento nuevo al final de nuestra lista, el máximo podría cambiar, con lo cual dejaría de ser válido, así que cada vez que se añada un elemento, pondremos el bit de suciedad de la operación máximo a verdadero1. Vamos a verlo (como de costumbre, es código en C#): //clase de ejemplo para ilustrar el uso
//de bits de suciedad
public class EjemploBitsSuciedad
{
//nuestro array de enteros
int[] v;
//un índice que nos lleve la cuenta
//de enteros en el array
int cuenta;
//el resultado de la operación máximo
int maximo;
//el bit de suciedad de la operación
//máximo
bool maximoSucio;
//El constructor
public
EjemploBitsSuciedad()
{
//damos dimensión al array
v=new int[1000];
//inicialmente vacío
cuenta=0;
//el resultado del máximo no es válido
maximo=int.MinValue;
maximoSucio=true;
}
//añadir un entero al final del array
public
void Agregar(int i)
{
v[cuenta]=i;
i++;
//ponemos el bit de suciedad de la operación
//máximo a true, porque si previamente teníamos
//un máximo válido, al insertar un nuevo
//elemento, ya no tienen por qué serlo
maximoSucio=true;
}
public
int ObtenerMaximo()
{
//sólo calculamos el máximo si está "sucio"
if (maximoSucio)
{
//este mensaje es sólo para ver
//cómo funciona esto
Console.WriteLine("VAMOS A CALCULAR EL MÁXIMO");
//en este bucle calculamos el máximo
foreach(int i in v)
{
if(i>maximo)
maximo=i;
} //foreach
//después de calcularlo, el máximo está "limpio"
maximoSucio=false;
} //if
else
{
//el else y este mensaje es sólo para ver como
//funciona esto
Console.WriteLine("EL MAXIMO ANTERIOR AUN VALE");
} //else
return maximo;
}
} //fin de la clase
Hagamos una prueba... Instanciamos la clase, añadimos unos enteros y vamos pidiendo el máximo de vez en cuando, a ver qué mensajes salen... public static void Main(string[] args)
{
EjemploBitsSuciedad ej=new EjemploBitsSuciedad();
ej.Agregar(3);
ej.Agregar(4);
ej.Agregar(9);
ej.Agregar(2);
Console.WriteLine(ej.ObtenerMaximo());
Console.WriteLine(ej.ObtenerMaximo());
ej.Agregar(10);
Console.WriteLine(ej.ObtenerMaximo());
Console.WriteLine(ej.ObtenerMaximo());
}
Por la consola observaremos ésta salida VAMOS A CALCULAR EL MAXIMO
9
EL MAXIMO ANTERIOR AUN VALE
9
VAMOS A CALCULAR EL MAXIMO
10
EL MAXIMO ANTERIOR AUN VALE
10
Es decir, la primera vez que pedimos el máximo después de agregar, realmente se calcula con el bucle, porque el resultado está "sucio". Al pedirlo una segunda vez sin agregar por enmedio, como el resultado está "limpio", no se calcula, sino que se devuelve el anterior. Además, la utilización del bit de suciedad es totalmente transparente a la hora de utilizar la clase. Esta técnica es ampliamente utilizada en situaciones como ésta, y sobre todo, en algunas que implican E/S, que suelen consumir gran cantidad de tiempo y recursos. Por ejemplo, obtener datos desde un SGBD, un servicio de red, etc. La técnica de los bits de suciedad también ayuda a paliar el efecto negativo del desliz que comentábamos hace unos días en el cual se incluyen operaciones costosas no necesarias en el interior de los bucles. 1: Ya sé que en este caso se puede ir calculando el máximo a medida que añadimos, pero ésto es sólo un ejemplo. |