Observador (en inglés: Observer) es un patrón de diseño de software que define una dependencia del tipo uno a muchos entre objetos, de manera que cuando uno de los objetos cambia su estado, notifica este cambio a todos los dependientes. Se trata de un patrón de comportamiento (existen de tres tipos: creación, estructurales y de comportamiento), por lo que está relacionado con algoritmos de funcionamiento y asignación de responsabilidades a clases y objetos.
Los patrones de comportamiento describen no solamente estructuras de relación entre objetos o clases sino también esquemas de comunicación entre ellos y se pueden clasificar en función de que trabajen con clases (método plantilla) u objetos (cadena de responsabilidad, comando, iterador, recuerdo, observador, estado, estrategia, visitante).
La variación de la encapsulación es la base de muchos patrones de comportamiento, por lo que cuando un aspecto de un programa cambia frecuentemente, estos patrones definen un objeto que encapsula dicho aspecto. Los patrones definen una clase abstracta que describe la encapsulación del objeto.
Este patrón también se conoce como el patrón de publicación-inscripción o modelo-patrón. Estos nombres sugieren las ideas básicas del patrón, que son: el objeto de datos, que se le puede llamar Sujeto a partir de ahora, contiene atributos mediante los cuales cualquier objeto observador o vista se puede suscribir a él pasándole una referencia a sí mismo. El Sujeto mantiene así una lista de las referencias a sus observadores. Los observadores a su vez están obligados a implementar unos métodos determinados mediante los cuales el Sujeto es capaz de notificar a sus observadores suscritos los cambios que sufre para que todos ellos tengan la oportunidad de refrescar el contenido representado. De manera que cuando se produce un cambio en el Sujeto, ejecutado, por ejemplo, por alguno de los observadores, el objeto de datos puede recorrer la lista de observadores avisando a cada uno. Este patrón suele utilizarse en los entornos de trabajo de interfaces gráficas orientados a objetos, en los que la forma de capturar los eventos es suscribir listeners a los objetos que pueden disparar eventos.
El patrón observador es la clave del patrón de arquitectura Modelo Vista Controlador (MVC).[1] De hecho el patrón fue implementado por primera vez en el MVC de Smalltalk basado en un entorno de trabajo de interfaz.[2] Este patrón está implementado en numerosos bibliotecas y sistemas, incluyendo todos los toolkits de GUI.
Patrones relacionados: publicador-subscriptor, mediador, singleton.
Definir una dependencia uno a muchos entre objetos, de tal forma que cuando el objeto cambie de estado, todos sus objetos dependientes sean notificados automáticamente. Se trata de desacoplar la clase de los objetos clientes del objeto, aumentando la modularidad del lenguaje, creando las mínimas dependencias y evitando bucles de actualización (espera activa o sondeo). En definitiva, normalmente, se usará el patrón observador cuando un elemento quiere estar pendiente de otro, sin tener que estar comprobando de forma continua si ha cambiado o no.
Si se necesita consistencia entre clases relacionadas, pero con independencia, es decir, con un bajo acoplamiento.
Puede pensarse en aplicar este patrón cuando una modificación en el estado de un objeto requiere cambios de otros, y no se desea que se conozca el número de objetos que deben ser cambiados. También cuando se quiere que un objeto sea capaz de notificar a otros objetos sin hacer ninguna suposición acerca de los objetos notificados y cuando una abstracción tiene dos aspectos diferentes, que dependen uno del otro; si se encapsulan estos aspectos en objetos separados se permitirá su variación y reutilización de modo independiente.
Habrá sujetos concretos cuyos cambios pueden resultar interesantes a otros y observadores a los que al menos les interesa estar pendientes de un elemento y en un momento dado, reaccionar ante sus notificaciones de cambio. Todos los sujetos tienen en común que un conjunto de objetos quieren estar pendientes de ellos. Cualquier elemento que quiera ser observado tiene que permitir indicar:
El observable tiene que tener, además, un mecanismo de aviso a los interesados.
A continuación se detallan a los participantes de forma desglosada:
La colaboración más importante en este patrón es entre el sujeto y sus observadores, ya que en el momento en el que el sujeto sufre un cambio, este notifica a sus observadores.
Las consecuencias de aplicar este patrón pueden ser tanto beneficiosas como pueden perjudicar algunos aspectos. Por una parte abstrae el acoplamiento entre el sujeto y el observador, lo cual es beneficioso ya que se consigue una mayor independencia y además el sujeto no necesita especificar los observadores afectados por un cambio. Por otro lado, con el uso de este patrón ocurre que se van a desconocer las consecuencias de una actualización, lo cual, dependiendo del problema, puede afectar en mayor o menor medida (por ejemplo, al rendimiento).
A continuación se detallan una serie de problemas que se pueden presentar a la hora de implementar este patrón:
Por ejemplo:
Secuencia incorrecta: a b c notificar() d e f Secuencia correcta: a b c d e f notificar()
Jerarquía con varios tipos des observadores: en este caso el hilo redefine cambios, no los notifica.
PULL: los objetos avisan de que han cambiado y el observador pregunta cuál ha sido el cambio.
PUSH: minimiza (eficiencia) que cuando algo cambia y se informará a todos los interesados, se realicen el menor número de llamadas posibles.
Soporte Java para el patrón Observador:
Clase java.util.Observable addObserver(o Observer) deleteObserver(o Observer) notifyObserver() notifyObservers(Object data)
Interface java.util.Observer void update (Observable o, Object data)
Los siguientes ejemplos muestran un programa que lee del teclado, y cada línea se tratara como un evento. Cuando una línea es obtenida, se llama al método que notificara a los observadores, tal que todos los observadores fueran avisados de la ocurrencia del evento mediante sus métodos de actualización.
En el caso de Java, el método de actualización seria notifyObservers.
import java.util.Scanner;
import java.util.Observable;
public class FuenteEvento extends Observable implements Runnable {
public void run() {
while (true) {
String respuesta = new Scanner(System.in).next();
setChanged();
notifyObservers(respuesta);
}
}
}
import java.util.Observable;
public class MiApp {
public static void main(String[] args) {
System.out.println("Introducir Texto: ");
FuenteEvento fuenteEvento = new FuenteEvento();
fuenteEvento.addObserver(new Observer(){
@Override
public void update(Observable obj, Object arg){
System.out.println("\nRespuesta recibida: " + arg);
}
});
new Thread(fuenteEvento).start();
}
}
En el lenguaje Python, el método de actualización seria notify. Un ejemplo más sencillo, pero parecido al anterior sería el siguiente:
class Listener:
def __init__(self, name, subject):
self.name = name
subject.register(self)
def notify(self, event):
print self.name, "received event", event
class Subject:
def __init__(self):
self.listeners = []
def register(self, listener):
self.listeners.append(listener)
def unregister(self, listener):
self.listeners.remove(listener)
def notify_listeners(self, event):
for listener in self.listeners:
listener.notify(event)
subject = Subject()
listenerA = Listener("<listener A>", subject)
listenerB = Listener("<listener B>", subject)
# El objeto Subject ahora tiene dos "escuchadores" registrados
subject.notify_listeners ("<event 1>")
# La salida obtenida es :
# <listener A> received event <event 1>
# <listener B> received event <event 1>