No son pocos los aspectos que hay que tener en cuenta al programar software: el producto final, por ejemplo, no solo ha de disponer de las funciones deseadas, sino que su código fuente también ha de ser legible y co­m­pre­n­si­ble. Na­tu­ra­l­me­n­te, para co­n­se­gui­r­lo, ha de in­ve­r­ti­r­se el menor esfuerzo posible, es­pe­cia­l­me­n­te si se de­sa­rro­llan programas o partes de ellos con funciones o elementos re­cu­rre­n­tes. Los llamados patrones GoF (Gang of Four) ofrecen para esta finalidad una gama de so­lu­cio­nes pre­de­fi­ni­das a diversos problemas típicos del diseño de software.

Al igual que otros patrones populares como el patrón Visitor y el patrón Singleton, el llamado patrón Observer también forma parte de esta colección de prácticos patrones de diseño que tanto facilitan el día a día de los pro­gra­ma­do­res. Te contamos en qué consiste el patrón Observer (in­clu­ye­n­do una re­pre­se­n­ta­ción gráfica en UML) y qué ventajas y de­s­ve­n­ta­jas presenta.

¿Qué es el patrón Observer?

El patrón de diseño Observer, Observer Pattern o patrón ob­se­r­va­dor es uno de los patrones de diseño de software más populares. Esta he­rra­mie­n­ta ofrece la po­si­bi­li­dad de definir una de­pe­n­de­n­cia uno a uno entre dos o más objetos para tra­n­s­mi­tir todos los cambios de un objeto concreto de la forma más sencilla y rápida posible. Para co­n­se­gui­r­lo, puede re­gi­s­trar­se en un objeto (observado) cualquier otro objeto, que fu­n­cio­na­rá como ob­se­r­va­dor. El primer objeto, también llamado sujeto, informa a los ob­se­r­va­do­res re­gi­s­tra­dos cada vez que es mo­di­fi­ca­do.

Como ya hemos me­n­cio­na­do, el patrón Observer es uno de los patrones GoF incluidos en el libro de 1994 Design Patterns: Elements of Reusable Object-Oriented Software. Las más de 20 so­lu­cio­nes de diseño descritas en esta pu­bli­ca­ción siguen teniendo un papel im­po­r­ta­n­te hoy en día en la co­n­ce­p­tua­li­za­ción y el de­sa­rro­llo de apli­ca­cio­nes in­fo­r­má­ti­cas.

Finalidad y fu­n­cio­na­mie­n­to del patrón Observer

El patrón Observer trabaja con dos tipos de actores: por un lado, el sujeto, es decir, el objeto cuyo estado quiere vigilarse a largo plazo. Por otro lado, están los objetos ob­se­r­va­do­res, que han de ser in­fo­r­ma­dos de cualquier cambio en el sujeto.

Hecho

Por lo general, un sujeto tiene asignados varios ob­se­r­va­do­res, pero, en principio, el patrón Observer también puede aplicarse con un único objeto ob­se­r­va­dor.

Sin el patrón Observer, los objetos ob­se­r­va­do­res tendrían que solicitar al sujeto re­gu­la­r­me­n­te que les enviase ac­tua­li­za­cio­nes acerca de su estado (status updates). Cada una de estas so­li­ci­tu­des co­n­lle­va­ría tiempo de co­mpu­tación y re­que­ri­ría, además, ciertos recursos de hardware. El patrón Observer se basa en la idea de ce­n­tra­li­zar la tarea de informar en manos del sujeto. Para co­n­se­gui­r­lo, existe una lista en la que los ob­se­r­va­do­res pueden re­gi­s­trar­se. En caso de mo­di­fi­ca­ción, el sujeto los informa uno tras otro, sin necesidad de que los ob­se­r­va­do­res lo pidan ac­ti­va­me­n­te. Si, más adelante, un ob­se­r­va­dor ya no necesita las ac­tua­li­za­cio­nes au­to­má­ti­cas, puede si­m­ple­me­n­te retirarse de la lista.

Nota

Para informar a cada uno de los ob­se­r­va­do­res pueden aplicarse dos métodos di­fe­re­n­tes. Con el método push, el sujeto indica di­re­c­ta­me­n­te en su mensaje qué cambios se han producido. Este método puede causar problemas si se tra­n­s­mi­ten in­fo­r­ma­cio­nes que el ob­se­r­va­dor no es capaz de procesar. El método pull, en cambio, no presenta este problema: con él, el sujeto tan solo transmite la in­fo­r­ma­ción de que se han producido cambios. Si los ob­se­r­va­do­res quieren saber de qué cambios se trata, han de solicitar el estado ac­tua­li­za­do mediante una llamada a método especial.

Re­pre­se­n­ta­ción gráfica del patrón Observer (diagrama UML)

El modo de fu­n­cio­na­mie­n­to y uso de los patrones de diseño, como el patrón Observer, suele ser difícil de entender para quienes no están fa­mi­lia­ri­za­dos con el tema. Para facilitar la co­m­pre­n­sión, puede ser útil observar una re­pre­se­n­ta­ción gráfica del patrón de diseño. El extendido lenguaje de mo­de­la­mie­n­to UML (Unified Modeling Language) se adecúa es­pe­cia­l­me­n­te a este propósito, ya que permite describir las re­la­cio­nes de forma co­m­pre­n­si­ble e intuitiva, tanto para los usuarios de la apli­ca­ción, como para los expertos. Por eso, hemos escogido UML como lenguaje de mo­de­la­ción para ofrecer la siguiente re­pre­se­n­ta­ción abstracta del patrón Observer.

¿Qué ventajas e in­co­n­ve­nie­n­tes presenta el patrón Observer?

El patrón Observer puede ser la solución adecuada a numerosos problemas de diseño. Su mayor ventaja es el alto grado de in­de­pe­n­de­n­cia entre el objeto observado (sujeto) y los objetos ob­se­r­va­do­res in­te­re­sa­dos en el estado actual del sujeto. El objeto observado no requiere ningún tipo de in­fo­r­ma­ción acerca de los ob­se­r­va­do­res, puesto que la in­ter­ac­ción se realiza de manera in­de­pe­n­die­n­te a través de la interfaz de los ob­se­r­va­do­res, que reciben ac­tua­li­za­cio­nes au­to­má­ti­ca­me­n­te. De esta manera, ya no se realizan so­li­ci­tu­des en vano (cuando el sujeto no se ha mo­di­fi­ca­do desde la última solicitud).

Sin embargo, las ac­tua­li­za­cio­nes au­to­má­ti­cas por parte del sujeto hacia todos los ob­se­r­va­do­res re­gi­s­tra­dos no siempre es una ventaja, ya que las in­fo­r­ma­cio­nes de cambios tra­n­s­mi­ti­das pueden ser irre­le­va­n­tes para ciertos ob­se­r­va­do­res. Este proceso supone un in­co­n­ve­nie­n­te es­pe­cia­l­me­n­te cuando el número de ob­se­r­va­do­res re­gi­s­tra­dos es muy alto, puesto que se malgasta mucho tiempo de co­mpu­tación. Otra de­s­ve­n­ta­ja del patrón Observer es que el código fuente del sujeto a menudo no revela qué ob­se­r­va­do­res están siendo in­fo­r­ma­dos.

¿Dónde se aplica el patrón ob­se­r­va­dor?

El patrón de diseño Observer se im­ple­me­n­ta sobre todo en apli­ca­cio­nes basadas en co­m­po­ne­n­tes cuyo estado,

  • por un lado, es muy observado por otros co­m­po­ne­n­tes
  • y, por otro, es mo­di­fi­ca­do re­gu­la­r­me­n­te.

Algunos de los casos de apli­ca­ción típicos de este patrón son las GUI (in­te­r­fa­ces gráficas de usuario), que actúan como interfaz de co­mu­ni­ca­ción de manejo sencillo entre los usuarios y el programa. Cada vez que se modifican los datos, estos deben ac­tua­li­zar­se en todos los co­m­po­ne­n­tes de la GUI. Esta situación es perfecta para la apli­ca­ción de la es­tru­c­tu­ra sujeto-ob­se­r­va­dor del patrón Observer. Incluso los programas que trabajan con conjuntos de datos en formato visual (ya sean tablas clásicas o diagramas gráficos) pueden be­ne­fi­ciar­se de la es­tru­c­tu­ra de este patrón de diseño.

En lo que respecta al lenguaje de pro­gra­ma­ción, el patrón Observer no conlleva, en principio, ninguna li­mi­ta­ción es­pe­cí­fi­ca. El único requisito es que el paradigma orientado a objetos sea co­m­pa­ti­ble con el patrón. Algunos de los lenguajes más uti­li­za­dos para im­ple­me­n­tar el patrón Observer son C#, C++, Java, Ja­va­S­cri­pt, Python y PHP.

Patrón Observer: ejemplo de apli­ca­ción

Puede llegar a haber grandes di­fe­re­n­cias en el modo de im­ple­me­n­ta­ción del patrón Observer según el lenguaje de pro­gra­ma­ción utilizado. Sin embargo, el concepto básico de la im­ple­me­n­ta­ción es siempre el mismo: el acceso a un objeto concreto (o a su estado) se pone a di­s­po­si­ción de muchos otros objetos. El tutorial del patrón Observer de ja­va­be­gi­n­ne­rs.de ofrece un ejemplo muy re­pre­se­n­ta­ti­vo de lo que ocurre en la práctica y que puede ser muy útil para co­m­pre­n­der este concepto.

En el ejemplo se quiere mostrar un texto publicado por el emisor en los campos de texto de varios re­ce­p­to­res. Para ello, la su­b­je­c­t­cla­ss (emisor, sujeto) añade a la ob­se­r­va­ble­cla­ss el método ad­dO­b­se­r­ver(). De este modo, pueden añadirse re­ce­p­to­res. Además, se introduce el método se­t­Cha­n­ged(), que registra cambios en el sujeto y hace una llamada a no­ti­f­yO­b­se­r­ve­rs() en caso de cambios, para informar así a todos los ob­se­r­va­do­res.

class emisor extends Observable {
	public emisor(){
		this.addObserver(new receptores_1());
		this.addObserver(new receptores_2());
		tell("Text");
	}
	public void tell(String info){
		if(countObservers()>0){
			setChanged();
			notifyObservers(info);
		}
	}
}

Los ob­se­r­va­do­res necesitan, además, una im­ple­me­n­ta­ción de la interfaz de ob­se­r­va­ción, in­clu­ye­n­do el método udpate() y dos ar­gu­me­n­tos: el objeto observado y el cambio en forma de instancia de objeto (Co­n­cre­te­Su­b­je­ct).

class receptores extends JFrame implements Observer{
	private JTextField field;
	public receptores (){
		field1 = new JTextField("a");
		add(field);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(300, 50);
		setVisible(true);
	}
	public void update(Observable o, Object arg) {
		field.setText((String) arg);
	}
}
Ir al menú principal