Artículos

miércoles, 24 de abril de 2013

Patrón Observer con Delegados y Eventos

Resumen

El patrón observer es un excelente recurso que todo buen desarrollador debe saber utilizar cuando se presente la ocasión.

La síntesis del patrón es que permite que un objeto se entere y reaccione (observador) ante el cambio en otro objeto (observado), sin generar un acoplamiento entre el observado y el observador (mecanismo de suscripción).

Supongamos este ejemplo de la vida real: 

Yo soy un objeto profesor y ustedes son objetos alumno. Ustedes están interesados en saber si mi propiedad "Va a dar clases" está en false para no venir a clases en tal caso. Entonces me llaman por teléfono para corroborar el estado de mi propiedad.
Pero como no saben en que momento esta propiedad puede pasar de estar en true a estar en false entonces me llaman varias veces al día (léase, consultan la propiedad varias veces).

Claramente esto produce un nivel de llamadas y estrés innecesario en el objeto profesor.

Entonces como estoy cansado les propongo un cambio.

Les pido que ya no me llamen para saber si voy a dar clases y me anoto el número de cada uno de ustedes para llamarlos si cambio de estado true a false.
Aquí mejoramos un poco pero igual seguimos con alto nivel de acoplamiento porque el objeto profesor tendría que tener una instancia de cada uno de ustedes para consultar la propiedad "teléfono" y llamarlos cuando ocurra el cambio en mi estado.

Finalmente encontramos la solución. Metemos entre nosotros un intermediario, la recepción del instituto y además agregamos en evento en mi objeto llamado "NoDoyClases" que lo disparo cuando algo me impida asistir al curso. Pero este evento es administrado (notificado) solo a este intermediario, es decir, a la recepción.
Cuando la recepción recibe el evento se fija todos los objetos alumnos que están suscriptos al evento (los observadores) y les notifica a cada uno que ocurrió el evento del profesor para que cada uno "reaccione" como quiera.

Implementación técnica en .Net

Supongamos que tenemos una clase impresora con un método imprimir que en cada llamada descuenta del total de papel disponible las hojas que se quieren imprimir.

namespace EjemploEventos
{
 
    //Declaro el intermediario al cual le voy a notificar el evento que necesito
    delegate void ManejadorDeEventos ();
 
    class Impresora
    {
 
        int cantidadHojas = 10;
 
        //Declaro el evento que se va a disparar cuando mi regla de negocio así lo indique
        public event ManejadorDeEventos NoHayMasPapel;
 
        
        // Método que aplica la regla de negocio que indica: "Disparar un aviso cuando la impresora se quede sin papel"        
        public bool Imprimir(int cantidad)
        {
            bool respuesta = false;
 
            if (cantidadHojas < cantidad)
                //Si se cumple esta condición disparo el evento
                NoHayMasPapel();
            else
            {
                cantidadHojas -= cantidad;
                respuesta = true;
            }
 
            return respuesta;
 
        }
 
    }
}

Ahora necesito un formulario que utilice un objeto de la impresora y "LO SUSCRIBO" al evento "NoHayMasPapel" para mostrar un mensaje por pantalla si no hay más papel.

namespace EjemploEventos
{
    public partial class FrmImpresion : Form
    {
        //Creo un campo con la impresosa
        Impresora objImpresora = new Impresora();
 
        public FrmImpresion()
        {
            InitializeComponent();
            //En el constructor me suscribo a los eventos que me interesa por medio del delegado 
            //y propongo un método como reacción ante la ocurrencia de ese evento
            objImpresora.NoHayMasPapel += new ManejadorDeEventos(objImpresora_NoHayMasPapel);
        }
 
        //Solo se dispara este código si el evento ocurre
        void objImpresora_NoHayMasPapel()
        {
            MessageBox.Show("No hay papel suficiente para esa cantidad de hojas.");
        }
 
        //Solo se dispara si el usuario presiona el botón
        private void button1_Click(object sender, EventArgs e)
        {
            if (objImpresora.Imprimir(int.Parse(textBox1.Text)))
                //Si se pudo disparar el evento entoces muestro el mensaje 
                MessageBox.Show("Impresion finalizada");
        }
    }
}




Conclusiones

Programar con el patrón Observer por medio de Delegados y Eventos permite crear una arquitectura basada en componentes independientes. Podemos tener objetos con estado (propiedades), operaciones (métodos) y reacciones (eventos).
Esto significa que puedo propagar la ejecución de código en otros componentes sin conocer o pensar en ellos al momento de crear la clase observada (dueña del evento).

Aquí les dejo el código fuente completo del proyecto para descargar.



https://skydrive.live.com/redir?resid=4285FB631C82DC8C!501&authkey=!AM9HFlz_uXOOTUE




Hasta la próxima entrega.

Diego Camacho
dcamacho31@hotmail.com