Patrones de Diseño (XIX): Patrones de Comportamiento - Mediator

Ya estamos en el ecuador de los patrones de comportamiento, y en esta ocasión vamos a hablar del patrón Mediator, cuya función como podéis intuir es actuar de mediador entre las interacciones que realizan los componentes de un sistema.

 

En la vida real tenemos cientos de ejemplos de este patrón. Por ejemplo si queremos enviar una carta a alguien no la entregamos nosotros o la vamos pasando a los vecinos a ver si algún día llegase al destino. En lugar de eso tenemos un intermediario, en este caso la oficina de correos, que se encarga de recorrer los mensajes y entregarlos al destino.

 

Vamos a ver detalladamente el concepto de Mediator mediante el análisis de su plantilla para concretar el concepto.

 

Mediator

 

Nombre: Mediator

 

Problema y Contexto:

En sistemas complejos en los que múltiples componentes actúan entre si, la complejidad de las dependencias puede crecer exponencialmente si tratamos de ampliarlo. Este escenario es típico de las interfaces gráficas y muchos otros prototipos software.

Además si hubiese que modificar un componente, habría que redefinir todas las dependencias explícitas en cada uno de los demás integrantes del sistema. También podemos ver las consecuencias que esto acarrearía si surge una excepción en uno de los componentes, esta se arrastraría a todos los que dependan de ella, y a su vez a los demás, pudiendo hacer caer el sistema si no se gestiona correctamente.

El contexto en el que actúa este patrón es en el de aplicaciones o sistemas cuyos integrantes se comunican activamente. Si la complejidad en la interacción es excesiva, encapsularla en una clase no contribuye demasiado al desacoplamiento, por lo que deberemos crear varios intermediarios.

 

Se aplica cuando:

  • Nuestro sistema tiene gran número de objetos que se comunican de forma activa, y dicha comunicación es compleja y está bien definida.
  • La reutilización de un objeto es difícil ya que es dependiente de muchos otros.
  • Si nuestro sistemas es excesivamente complejo deberemos subdividirlo en varios Mediator.

Solución y Estructura:

La solución consiste en crear una entidad intermediaria que se encargue de gestionar la comunicación entre objetos. En primer lugar definiremos una interfaz para exponer las operaciones que un intermediario puede realizar, la cual llamaremos Mediator. Como es evidente debemos implementar dicha interfaz mediante una clase ConcreteMediator para dotar a éste de funcionalidad.

El siguiente paso es definir la interfaz de los integrantes del sistema, la cual llamaremos Colleague (que significa literalmente colega). Aquí se expondrán todas las operaciones que un objeto perteneciente al sistema puede realizar para comunicarse. Podemos crear diferentes tipos de colegas siempre y cuando respeten la interfaz.

 

La estructura es la siguiente:

 

(Ver la imagen del comienzo)

 

Donde:

 

Mediator: Establece las operaciones que pueden realizarse sobre el sistema encapsulado.

 

ConcreteMediator: Implementa las operaciones anteriores y define la interacción de los elementos del sistema. Actúa de procesador central.

 

Colleague: Establece las operaciones comunes que todo integrante del sistema debe implementar.

 

ConcreteColleague: Es el integrante real del sistema que implementa todas las operaciones establecidas por la interfaz Colleague y las suyas propias.

 

Consecuencias:

POSITIVAS:

  • Desacopla a los integrantes del sistema de sus interacciones. De esta manera podrían reutilizarse o modificar los Colleague y los Mediator de manera independiente.
  • Simplifica la comunicación entre objetos: los objetos que se comunican de "muchos a muchos" ahora lo harán de "uno a muchos", disminuyendo su complejidad.
  • Si se plantea correctamente, la interacción entre los integrantes es más fácil de entender.

 

NEGATIVAS:

  • Si los sistemas son demasiado grandes, las ventajas en materia de simplicidad y facilidad de modificación desaparecen.

 

Patrones Relacionados: Facade, Adapter y Observer.

 

Ejemplo:

 

Vamos a ver un ejemplo de un sistema que emplea Mediator. Nuestro ejemplo es una analogía de la vida real en internet relacionada con los portales web: cualquier usuario puede compartir publicaciones con los demás y comentar las mismas. Además un usuario puede registrarse en el portal, mandar mensajes privados a otros usuarios y suscribirse a las publicaciones de otros. Vamos a ver cómo expresamos esto en Java: 

 

// En primer lugar definiremos la interfaz Colleague, que
// definirá las operaciones que un usuario puede realizar

public interface Usuario {
	public int publicarArticulo(String titulo, String texto);
	public int comentarArticulo(Usuario usuario, int publicacion, String texto);
	public boolean mensajePrivado(Usuario destino, String texto);
	public boolean suscribirse(Usuario usuario);
	public void leerPublicacion(Usuario usuario, int publicacion);
	public void leerComentario(Usuario usuario, int publicacion, int comentario);
	public void recibirMensaje(Usuario usuario, String texto);
	public void recibirNotificacion(String texto);
}

// A continuación definiremos el Mediator, que en este caso es
// representado por el Portal web

public interface Portal {
	public void registrar(Usuario usuario);
	public int publicar(Usuario usuario, String titulo, String texto);
	public int comentar(Usuario usuario, Usuario publicador, int publicacion, String texto);
	public void mensaje(Usuario usuario, Usuario destino, String texto);
	public void suscribir(Usuario usuario, Usuario publicador);
	public String consultarPublicacion(Usuario usuario, Usuario publicador, int publicacion);
	public String consultarComentario(Usuario usuario, Usuario publicador, int publicacion, int comentario);
}

// Ahora vamos a implementar la interfaz Usuario

public class UsuarioPortal implements Usuario {
	private void Portal portal;
	private String nombre;

	public UsuarioPortal(Portal portal, String nombre) {
		this.portal = portal;
		portal.registrar(this);
	}

	public int publicarArticulo(String titulo, String texto) {
		portal.publicar(this, titulo, texto);
	}

	public int comentarArticulo(Usuario usuario, int publicacion, String texto) {
		portal.comentar(this, usuario, publicacion, texto);
	}

	public boolean mensajePrivado(Usuario destino, String texto) {
		portal.mensaje(this, destino, texto);
	}

	public boolean suscribirse(Usuario usuario) {
		portal.suscribir(this, usuario);
	}

	public void leerPublicacion(Usuario usuario, int publicacion) {
		System.out.println(portal.consultarPublicacion(this, usuario, publicacion));
	}

	public void leerComentario(Usuario usuario, int publicacion, int comentario) {
		System.out.println(portal.consultarComentario(this, usuario, publicacion, comentario));
	}

	public void recibirMensaje(Usuario usuario, String texto) {
		System.out.println("NUEVO MENSAJE DE "+usuario+" : n" + texto);
	}
	
	public void recibirNotificacion(String texto) {
		System.out.println("NUEVA NOTIFICACIÓN : n" + texto);
	}

	@Override
	public String toString()
	{
		return this.nombre;
	}
}

// Y ahora hacemos lo propio con portal


public class ProgramacionNet implements Portal{

	private ArrayList usuarios;
	private ArrayList publicaciones;
	private ArrayList comentarios;
	private HashMap<Integer, Usuario> publicaciones_usuario;
	private HashMap<Integer, Integer> comentarios_publicaciones;
	private HashMap<Usuario, ArrayList> suscripciones;

	public ProgramacionNet() {
		usuarios = new ArrayList();
		publicaciones = new ArrayList();
		comentarios = new ArrayList();
		publicaciones_usuario = new HashMap<Integer, Usuario>();
		comentarios_publicaciones = new HashMap<Integer, Integer>();
		suscripciones = new HashMap<Usuario, ArrayList>();
	}

	public void registrar(Usuario usuario) {
		usuarios.add(usuario);
		suscripciones.put(usuario, new ArrayList());
	}

	public int publicar(Usuario usuario, String titulo, String texto) {
		if (!usuarios.contains(usuario))
			return -1;
		publicaciones.add(titulo + 'n' + texto);
		int pub_id = publicaciones.size() - 1;
		publicaciones_usuario.put(pub_id, usuario);
		notificar(usuario, publicaciones.get(pub_id));
		return pub_id;
	}

	public int comentar(Usuario usuario, Usuario publicador, int publicacion, String texto) {
		if (!(usuarios.contains(usuario) && usuarios.contains(publicador)))
			return -1;
		comentarios.add(texto);
		int com_id = comentarios.size() - 1;
		comentarios_publicaciones.put(com_id, publicacion);
		return com_id;
	}

	public void mensaje(Usuario usuario, Usuario destino, String texto) {
		if (!(usuarios.contains(usuario) && usuarios.contains(destino)))
			return;
		destino.recibirMensaje(usuario, texto);
	}

	public void suscribir(Usuario usuario, Usuario publicador) {
		if (!(usuarios.contains(usuario) && usuarios.contains(publicador)))
			return;
		suscripciones.get(publicador).add(usuario);
	}

	public String consultarPublicacion(Usuario usuario, Usuario publicador, int publicacion) {
		if (!(usuarios.contains(usuario) && usuarios.contains(publicador)))
			return "No existe el usuario "+publicador;
		if (!comentarios_publicaciones.containsKey(publicacion) || !publicaciones_usuario.get(publicacion).equals(publicador) )
			return "No existe la publicacion";
		return publicaciones.get(publicacion);
	}

	public String consultarComentario(Usuario usuario, Usuario publicador, int publicacion, int comentario) {
		if (!(usuarios.contains(usuario) && usuarios.contains(publicador)))
			return "No existe el usuario "+publicador;
		if (!publicaciones_usuario.containsKey(publicacion) || !publicaciones_usuario.get(publicacion).equals(publicador) )
			return "No existe la publicacion";
		if (!comentarios_publicaciones.containsKey(comentario) || comentarios_publicaciones.get(comentario) != publicacion )
			return "No existe el comentario";
		return comentarios.get(comentario);
	}

	private void notificar(Usuario usuario, String texto) {
		ArrayList suscriptores = suscripciones.get(usuario);
		for (int i = 0; i< suscriptores.size(); i++) {
			suscriptores.get(i).recibirNotificacion(usuario + 
				" ha escrito una nueva publicación:n" +
				texto);
		}
	}
}

// Finalmente vamos a ver como funciona todo en el main

public static void main(String[] args) {
	Portal programacionnet = new ProgramacionNet();

	Usuario juan = new UsuarioPortal(programacionnet, 'Juan');
	Usuario ana = new UsuarioPortal(programacionnet, 'Ana');
	Usuario felipe = new UsuarioPortal(programacionnet, 'Felipe');

	juan.suscribirse(ana);
	felipe.suscribirse(ana);

	int publicacion_ana = ana.publicarArticulo("Patrón Mediator","Ejemplo de patrón Mediator");
	int comentario_juan = juan.comentarArticulo(ana, publicacion_ana, "Muy bueno. Es bastante útil.");
	int comentario_felipe = felipe.comentarArticulo(ana, publicacion_ana, "¿Qué es todo esto?");

	System.out.println(juan.leerComentario(ana, publicacion_ana, comentario_juan);
	ana.mensajePrivado(juan, "Gracias por comentar.");
	felipe.mensajePrivado(ana, "¿Me das tu Whatsapp?");
	ana.mensajePrivado(felipe, "Esto es un portal de programación, no de citas.");
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP