En este sencillo pero emotivo acto, doy por inaugurada la sección Herramientas en NuevosProgramadores.com, una página en la que voy a ir listando herramientas que son muy útiles a la hora de programar y que muchas veces cuando conocí alguna de ellas pensé: Si hubiese conocido esto antes!
Si alguien conoce alguna herramienta que encaje en esta lista, que me lo haga saber así puedo agregarla inmediatamente.
}
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;
}
}
}
Muchas veces nos encontramos con situaciones en las que nos vendría muy bien poder devolver más de un valor en una función… generalmente podemos recurrir a arrays, colecciones, diccionarios, hastables, etc. Pero en Python podemos devolver múltiples valores en una misma función y lo mejor de todo es que esos valores de retorno pueden ser de diferente tipo.
Dejo un par de ejemplos, escribo la función y luego la ejecuto para probar.
def masUnoMasDos(x):
return x+1, x+2
print ""
print "masUnoMasDos()"
a,b = masUnoMasDos(7)
print "x+1 = "+str(a)
print "x+2 = "+str(b)
print "---------"
def rojo():
return 255, 0, 0
print ""
print "rojo()"
print rojo()
print "---------"
def rojoHexa():
return "ff", "00", "00"
print ""
print "rojoHexa()"
print rojoHexa()
Obtendremos la siguiente salida:
masUnoMasDos()
x+1 = 8
x+2 = 9
---------
rojo()
(255, 0, 0)
---------
rojoHexa()
('ff', '00', '00')
Para probar este código rápidamente, en caso de no tener Python instalado pueden utilizar http://codepad.org que es una excelente herramienta online, la misma permite compilar y ejecutar código en varios lenguajes, incluyendo: C/C++, D, Haskel, PHP, Pearl, Python y Ruby, entre otros.
}
Algo muy común a la hora de refactorear es que se elimine algún método o alguna clase, pero si estamos trabajando en un grupo de soluciones con referencias entre ellas, eliminar un método puede causar bastantes problemas… y molestias en quienes estén trabajando con nuestro código.
.Net ofrece una ayuda, es un atributo que se puede aplicar a clases, métodos, propiedades, variables y constantes y que sirve de alternativa para no romper compatibilidad, el attributo Obsolete() nos permite marcar como obsoleta una porción del código sin eliminarlo y nos brinda la posibilidad de mostrar un mensaje y generar una advertencia o bien un error a la hora de compilar.
Veamos un pequeño ejemplo en VB.Net y luego en C#
Public Class MiClase
<Obsolete("Este constructor fue reemplazado por New(String, Boolean)", True)> _
Public Sub New(Arg1 as String, Arg2 as String)
' Código del ctor obsoleto
' Ahora genera un error al compilar
End Sub
Public Sub New(Arg1 as String, Arg2 as Boolean)
'Código del nuevo ctor
End Sub
<Obsolete("Esta propiedad está obsolete. Por favor eliminar toda referencia a la misma antes del release 2.0")> _
Public ReadOnly Property IsObsolete() As Boolean
Get
Return True
End Get
End Property
End Class
class Ejemplo
{
[Obsolete()]
public const string MI_CONSTANTE = "NuevosProgramadores.com";
[Obsolete("No usar esta variable. Utilizar la variable FechaHoraActual en su lugar.", false)]
DateTime Fecha = DateTime.Now;
}
A simple vista se puede ver que la única diferencia entre su uso en VB.net y C# es que los atributos se aplican de manera diferente.
Por último veamos las 3 firmas de este atributo:
- Obsolete() - Genera una advertencia (warning) sin descripción alguna.
- Obsolete(String) - Genera una advertencia con con la descripción indicada en el parámetro.
- Obsolete(String, Boolean) - Utiliza el parámetro String como descripción y el Boolean indica si genera una advertencia (false) o un error (true).
}