¿Que es el patrón de diseño?
En palabras simples es una forma o estilo solución para un problema dado o conocido en la creación de software. Estos patrones son soluciones probadas por lo que en vez de ponerse uno a solucionar un problema desde 0, puede usar uno de estos patrones ya creados.
Existe el libro maestro, un tanto antiguo pero de todas formas es considerado un biblia, el libro es conocido como GoF o Gang-Of-Four ("La banda de los Cuatro" seguramente por que lo escribieron 4 autores). Se puede comprar acá por casi 40 dólares, está en ingles:
https://www.amazon.com/Design-Patterns-Object-Oriented-Addisonwesley-Professional/dp/0201633612
Existe el libro maestro, un tanto antiguo pero de todas formas es considerado un biblia, el libro es conocido como GoF o Gang-Of-Four ("La banda de los Cuatro" seguramente por que lo escribieron 4 autores). Se puede comprar acá por casi 40 dólares, está en ingles:
https://www.amazon.com/Design-Patterns-Object-Oriented-Addisonwesley-Professional/dp/0201633612
¿Que es el patrón de diseño Observer?
Es un patrón clasificado dentro del grupo de comportamiento (hay 3 tipos de patrones) que permite que un objeto X le avise a N subscriptores de algún cambio de estado. De esta forma los subscriptores no tienen que preguntar eternamente por algún cambio sino que el objeto X, ahora llamado Sujeto, le informa su cambio a los subscriptores, ahora llamados Observadores. Además de realizarse una sincronización de todos los observadores con ese valor.Si separamos un poco la estructura tenemos estos 5 elementos:
- Un Sujeto: es una clase abstracta (por lo que otra clase heredará de ella y la utilizará), permite básicamente 3 cosas, registrar o subscribir un observador, de-suscribirlo, notificar a los observadores de algún cambio.
- Un Sujeto real o Sujeto concreto: Es objeto real con el cual se trabajará desde otras clases, por lo que hereda de Sujeto y utiliza sus métodos.
- Un Observador: Es una interfaz (en el código será IObservador) que contiene las acciones que puede realizar un Observador real. Por ahora, solo tiene una acción: Actualizar(valor) por pantalla. Ojo que al ser una interfaz, no implementa esta acción, solo las declara.
- N Observadores reales o concretos: Estas son objetos que representan tus clases propias, como Cliente, Producto, etc. Esta clase implementa la acción Actualizar(valor) de la interfaz Observador. Puse N ya que es sólo 1 una clase en verdad, pero puedes tener N instancias de ella en el programa principal que hará el llamado a crear estas instancias.
- Programa o main: a manera de ejemplo la tenemos, en su caso puede que tengan una clase de negocio o algo más complejo.
He comparado el modelo de clases de diferentes artículos o libros y la base es la misma, pero varia un poco entre cada una de ellas. Nosotros haremos nuestra propia versión que tiene la base del modelo indicado en el libro "Gang of Four":
Modelo de clases según Wikipedia en inglés.
Modelo de clases según Wikipedia en español.
Nuestro modelo que implementaremos, vista Diagrama de clases de Visual Studio 2012.
Dejando la cháchara, vamos a meter los dedos.
Metiendo los deditos
El 1er. objeto crear es Observador, que es una interfaz que le llamaremos IObservador:
El 2do. elemento es la clase Observador concreto o real que implementará el método Actualiza de la interfaz. Define una variable valor que es compartido por el resto de los otros Observadores concretos y que debe sincronizarse en caso de un cambio por parte del Sujeto con tal de que todos los Observadores tengas el mismo valor:
El 3er. elemento es la clase Sujeto, que es abstracta, y usa métodos del Observador concreto creado recientemente:
using System; namespace ConsoleApplication1 { // Observador es una interfaz que define que método que el ObservadorConcreto debe implementar interface IObservador { void Actualiza(string valor); } }
El 2do. elemento es la clase Observador concreto o real que implementará el método Actualiza de la interfaz. Define una variable valor que es compartido por el resto de los otros Observadores concretos y que debe sincronizarse en caso de un cambio por parte del Sujeto con tal de que todos los Observadores tengas el mismo valor:
using System; namespace ConsoleApplication1 { // Esta clase es tu clase "producto", "cliente" o como le llames, yo le // puse así ya que conceptualmente es un Observador real que implementa // el método definido en la interfaz. // Tiene la propiedad compartida por el resto de los observadores (en este // caso una propiedad llamada Valor) y que es la que los otros // observadores quieren // monitorear, también actualiza su valor e imprime por pantalla el // cambio si el Sujeto le dice que lo haga. class ObservadorContreto: IObservador { string nombre; string valor = "0"; //por defecto //Constructor: definimos un nombre para el observador real public ObservadorContreto(string nombre) { this.nombre = nombre; } // Notifica y actualiza el nuevo valor en este ObservadorConcreto // e imprime en pantalla. public void Actualiza(string valor) { // Actualizamos el valor con tal de sincronizar this.valor = valor; Console.WriteLine("Se notifica a {0} que Sujeto ha cambiado el Valor a {1}", nombre, valor); } } }
El 3er. elemento es la clase Sujeto, que es abstracta, y usa métodos del Observador concreto creado recientemente:
using System; using System.Collections; namespace ConsoleApplication1 { // Clase abstracta que almacena una colección de observadores subscritos // y los notifica de algún cambio, le dice a cada ObservadorConcreto // subscrito que actualize el valor e imprima en pantalla el cambio. // Es responsabilidad de cada ObservadorConcreto imprimir en pantalla, // pero es responsabilidad del Sujeto indicarles a ellos que lo hagan. abstract class Sujeto { ArrayList coleccionObservadores = new ArrayList(); public void RegistraObservador(ObservadorContreto observador) { coleccionObservadores.Add(observador); } public void DesRegistraObservador(ObservadorContreto observador) { coleccionObservadores.Remove(observador); } public void NotificaAObservadores(string valor) { foreach (ObservadorContreto observadores in coleccionObservadores) { observadores.Actualiza(valor); } } } }
El 4to. elemento es la clase Sujeto concreto o real, que hereda de Sujeto:
using System; namespace ConsoleApplication1 { // Clase real que implementa el Sujeto. Esta clase es usada para // indicar que cualquier cambio afecta a las otros observadores class SujetoContreto: Sujeto { public void CambiaValor(string valor) { NotificaAObservadores(valor); } } }
El 5to. y último elemento es la clase Programa o Main, que utiliza la clase SujetoConcreto para registrar Observadores y cambiar un valor que se replicará o sincronizará en cada uno de ellos y se notificará en pantalla. Luego de des-registran 2 Observadores y cambia de nuevo el valor para ver a quien informa:
using System; namespace ConsoleApplication1 { class Programa { static void Main(string[] args) { SujetoContreto sujetoConcreto = new SujetoContreto(); // Creamos 4 observadores concretos o "sapos" ObservadorContreto observdor1 = new ObservadorContreto("Sapo1"); ObservadorContreto observdor2 = new ObservadorContreto("Sapo2"); ObservadorContreto observdor3 = new ObservadorContreto("Sapo3"); ObservadorContreto observdor4 = new ObservadorContreto("Sapo4"); // SujetoConcreto le dice a Sujeto que agrege un nuevo // ObservadorConcreto a su colección. Esto lo hace el Sujeto // sin derivarle esta tarea a nadie más sujetoConcreto.RegistraObservador(observdor1); sujetoConcreto.RegistraObservador(observdor2); sujetoConcreto.RegistraObservador(observdor3); sujetoConcreto.RegistraObservador(observdor4); // Por medio de SujetoConcreto decimos que cambiará el valor, // pero lo que por abajo pasa es que el SujetoConcreto // le dice al Sujeto // que actualice el valor, y el Sujeto lo // que hace es decirle a cada ObservadorConcreto que lo haga sujetoConcreto.CambiaValor("10"); Console.WriteLine(""); // Sacamos a 2 ObservadoresConcretos, dejando solo a 2. // Esto lo hace el Sujeto en verdad sin darle la tarea a nadie más sujetoConcreto.DesRegistraObservador(observdor3); sujetoConcreto.DesRegistraObservador(observdor4); // Hacemos otro cambio de valor sujetoConcreto.CambiaValor("20"); Console.Read(); } } }
Salida
La salida del test nos da:Se notifica a Sapo1 que Sujeto ha cambiado que el Valor a 10
Se notifica a Sapo2 que Sujeto ha cambiado que el Valor a 10
Se notifica a Sapo3 que Sujeto ha cambiado que el Valor a 10
Se notifica a Sapo4 que Sujeto ha cambiado que el Valor a 10
Se notifica a Sapo1 que Sujeto ha cambiado que el Valor a 20
Se notifica a Sapo2 que Sujeto ha cambiado que el Valor a 20