Nov 19 2008

Patrón Decorador - Definición, diagramas e implementación {

Tag: Decorador, Java, Patrones

El patrón decorador, (decorator pattern), añade nuevas responsabilidades a un objeto dinamicamente, ofreciendo una alternativa flexible a la herencia.

En otras palabras, es una forma de agregar funcionalidades a una clase, extendiéndola pero sin recurrir a la herencia. ¿Por qué no heredar? Bueno, tal vez no desees dar acceso a toda la clase primaria y entonces sólo se daría acceso a lo necesario.

Veamos un ejemplo simple, tenemos las siguientes clases:

  • Persona (ID, Nombre, Apellido y Fecha de Nacimiento)
  • Usuario (ID, Nombre, Apellido, Usuario, Passwd)
  • Cliente (ID, Nombre, Apellido, Teléfono, Email)
  • Cliente VIP (ID, Nombre, Apellido, Teléfono, Email, Nro de Cuenta, Límite de Crédito)

Cómo vemos todos tienen campos comunes a Persona, pero no tienen todos los campos de la clase Persona, por eso no podemos heredar, así es que podremos implementar decoradores para la clase Persona, tendremos los decoradores Usuario, Cliente y Cliente VIP. (Ver figura 1).

Pero si analizamos lo que hemos diseñado hasta el momento podremos observar que Cliente VIP podría llegar a convertirse en un decorador de Cliente, que como recordamos es un decorador de Persona, de esa manera no tendremos que reescribir la lógica detrás de los atributos Teléfono e Email. (Ver figura 2).

La pregunta es cómo decorar una clase, pues a grandes rasgos podríamos decir que se trata de tener una variable privada de la clase a decorar, escribir los métodos o atributos que se deseen publicar de la clase a decorar y luego agregar las decoraciones en nuestra nueva clase, entendiendo por decoraciones los nuevos métodos y/o atributos.

Por último veamos el código correspondiente a nuestras clases.

class Persona
{
	private int ID;
	private String Nombre;
	private String Apellido;
	private Date FechaNac;
 
	public int getID()
	{
		return ID;
	}
 
	public String getNombre()
	{
		return Nombre;
	}
 
	public String getApellido()
	{
		return Apellido;
	}
 
	public Date getFechaNac()
	{
		return FechaNac;
	}
}
 
class Usuario
{
	private Persona mPersona;
 
	private String Usuario;
	private String Passwd;
 
	public int getID()
	{
		return mPersona.getID();
	}
 
	public String getNombre()
	{
		return mPersona.getNombre();
	}
 
	public String getApellido()
	{
		return mPersona.getApellido();
	}
 
	public String getUsuario()
	{
		return Usuario;
	}
 
	public String getPasswd()
	{
		return Passwd;
	}
}
 
class Cliente
{
	private Persona mPersona;
 
	private String Telefono;
	private String Email;
 
	public int getID()
	{
		return mPersona.getID();
	}
 
	public String getNombre()
	{
		return mPersona.getNombre();
	}
 
	public String getApellido()
	{
		return mPersona.getApellido();
	}
 
	public String getTelefono()
	{
		return Telefono;
	}
 
	public String getEmail()
	{
		return Email;
	}
}
 
class Cliente_VIP
{
	private Cliente mCliente;
 
	private String NroCuenta;
	private Double LimiteCredito;
 
	public int getID()
	{
		return mCliente.getID();
	}
 
	public String getNombre()
	{
		return mCliente.getNombre();
	}
 
	public String getApellido()
	{
		return mCliente.getApellido();
	}
 
	public String getTelefono()
	{
		return mCliente.getTelefono();
	}
 
	public String getEmail()
	{
		return mCliente.getEmail();
	}
 
	public String getNroCuenta()
	{
		return NroCuenta;
	}
 
	public Double getLimiteCredito()
	{
		return LimiteCredito;
	}
}

}


Sep 25 2008

Convertir un entero base 10 a binario en Java (Recursivo) {

Tag: Algoritmos, Java

En este ejemplo tenemos una aplicación simple que pide un número y lo convierte a binario utilizando un algoritmo recursivo con una variable externa.

Lo primero que tenemos que saber es cómo convertir un número de base 10 a binario, (base 2). Para ello, se divide el número deseado entre 2 y cada resultado de la división tantas veces como sea necesario hasta obtener el número indivisible, el 1. Luego se toman el último resultado, 1 y todos los restos de las divisiones, viendo la imagen va a quedar mucho más claro el concepto.
Divisiones necesarias para convertir un número a binario

A continuación veremos el código en Java necesario para pedir el número en base 10 y devolverlo en binario, por supuesto que se pueden agregar validaciones, de hecho hay que agregar validaciones para asegurarnos que el usuario ingrese un número, pero ese no es el objetivo de este post.

import java.io.BufferedReader;
import java.io.InputStreamReader;
 
class binario
{
	public static void main(String[] args)
	{
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader rdr = new BufferedReader(isr);
		System.out.print("Ingrese un número entero: ");
		String buffer = "0";
		try
		{
			buffer = rdr.readLine();
		}
		catch(java.io.IOException e) // Es obligatorio manejar la IOException, si se omite no compila
		{
			System.out.println(e);
		}
 
		int dec = Integer.parseInt(buffer);
		System.out.print("El valor binario correspondiente es: ");
 
		if(dec == 0)
			System.out.println(dec); // Cero en cualquier base es cero
		else
			System.out.println(obtenerBinario(dec)); // llamamos a la func. recursiva
	}
 
 
	static String aux = ""; // variable auxiliar utilizada para almacenar el resto de cada división
 
	static String obtenerBinario(int decimal)
	{
		if(decimal == 1) // Límite de la recursividad
			return Integer.toString(decimal) + aux; // Devolvemos 1 y los restos anteriores
		else
		{
			aux = aux + Integer.toString(decimal % 2); //guardamos el resto en la variable auxiliar
			return obtenerBinario(decimal / 2); // hacemos el llamado recursivo con el resultado de la división
		}
	}
}

Además del algoritmo para convertir los números, vale la pena ver la forma en que se lee la entrada estándar, utilizando un BufferedReader, pero eso lo veremos con mayor profundidad en otro post.

}


Sep 18 2008

Patrón Estrategia: Definición y ejemplo en Java {

El patrón Estrategia (Strategy) define una familia de algoritmos, los encapsula y hace intercambiables. Permitiendo a cada algoritmo variar independientemente de los clientes que lo utilizan.

Se utiliza para encapsular aquellas partes del código que varían, haciendo que éstas variaciones no afecten al resto del código, evitando así consecuencias inesperadas y brindando a nuestro código una mayor flexibilidad.

Para ilustrarnos con un ejemplo, supongamos que tenemos un grupo de terminales las cuales tienen su propio método de entrada y salida de datos, por ej. teclado, monitor, mouse, etc.
Supongamos que en éste momento sólo tenemos 3 métodos de entrada y 2 de salida, pero sabemos que en el futuro se van a agregar más, por ello recurriremos al Patrón Estrategia para encapsular los métodos de entrada y salida, permitiéndonos en el futuro implementar las nuevas terminales con gran facilidad y sin afectar a las ya existentes.

Para encapsular los métodos de entrada recurriremos al uso de interfaces, crearemos una interface Entrada y una Salida, las cuales serán implementadas por cada método de entrada/salida de datos. A su vez, necesitamos tener varias terminales y poder agregar los nuevos tipos, para ello utilizaremos herencia, todas nuestras terminales heredarán de la superclase Terminal.

La clase Terminal tendrá una variable de instancia del tipo Entrada (interface) y una del tipo Salida (interface).
Además crearemos una clase por cáda método de entrada y salida las cuales implementarán la interface correspondiente. También podremos definir una clase, por ejemplo Monitor_TouchScreen que implemente ambas interfaces, por lo que podremos utilizarlo como método de entrada y salida.

En los diagramas vemos las interfaces Entrada y Salida con las clases que la implementan, y la superclase Terminal con sus subclases.

Ahora podemos ir un paso más allá y crear una Terminal Mutable, (qué nombre, no?), por ese nombre me refiero a una Terminal que pueda cambiar sus método de entrada y/o salida en tiempo de ejecución, lo cual suena mucho más complejo de lo que realmente es, ya que la única diferencia con las terminales que hemos creado hasta el momento es que en esta tendremos que definirnos un setter para el método de entrada y uno para el método de salida, mientras que el resto no varía.

Luego de descargar esta implementación en Java hay que ir al directorio bin/ y ejecutar java TestTerminales

Ejemplo de Patrón Estrategia en Java (7.72 KB)

}


Página 1 de 11