Decorator Pattern (VFP)

(Difference between revisions)
Jump to: navigation, search
(Encadenando decoradores)
Line 174: Line 174:
 
</source>
 
</source>
  
El truco esta en que la clase '''DecoratorPattern''' recibe un parámetro opcional en su método constructor (Init).  Este parámetro contiene la instancia de la clase que se desea decorar. Si no se indica (como en los ejemplos anteriores), la clase usa el valor de la propiedad '''className''' para crear una instancia de dicha clase automáticamente.  Esta facilidad permite incluso lograr otros efectos interesantes con la clase, como por ejemplo, el lograr declarar un decorador '''generico''' que permita implementar una cierta interfaz en cualquier otra clase. Por ejemplo, supongamos que tenemos esta [Implementacion_de_interfaces_en_VFP interfaz] que permite serializar y deserializar objetos:
+
El truco esta en que la clase '''DecoratorPattern''' recibe un parámetro opcional en su método constructor (Init).  Este parámetro contiene la instancia de la clase que se desea decorar. Si no se indica (como en los ejemplos anteriores), la clase usa el valor de la propiedad '''className''' para crear una instancia de dicha clase automáticamente.  Esto permite aplicar funcionalidad adicional a una clase en forma aditiva, es decir, segun lo que se necesite (lo cual es una de las ventajas de esta tecnica sobre el uso de subclases).
 
+
<custom lang="visualfoxpro">
+
DEFINE CLASS ISerializable AS Interface
+
  PROCEDURE ToString
+
  PROCEDURE initWithString
+
ENDDEFINE
+
</custom>
+

Revision as of 07:55, 15 April 2012

Por: VictorEspina

Contents

Introducción

El patrón de diseño Decorador (Decorator Pattern) es una técnica que permite extender la funcionalidad de una clase sin hacer uso de la herencia. Si bien siempre ha habido una discusion sobre cuando usar Decoradores en lugar de subclases, lo cierto es que hay muchas ocasiones en donde el uso de un decorador puede solucionar un requerimiento dado y evitar el crecimiento desmedido del arbol de herencia en una clase dada.

Adicionalmente, el uso de decoradores anidados permite simular la función de multiherencia en lenguajes donde esta característica no es soportada (como es el caso de VIsual FoxPro).

Como funciona un Decorador

Un decorador no es mas que una clase que expone todos los métodos y propiedades de otra clase, ademas de los propios. Supongamos que tenemos una clase Usuario que implementa la siguiente interfaz:

DEFINE CLASS Usuario AS Custom
 Login = ""
 Password = ""
 nombreCompleto = ""
 
 PROCEDURE verificarPassword
 PROCEDURE Login
 PROCEDURE Logout
ENDDEFINE


Como vemos, esta clase representa los datos básicos de un usuario y las operaciones que este puede realizar. Supongamos que ahora queremos incluir la posibilidad de que el usuario pueda cambiar su password. Una forma de hacer esto seria modificar la definición de la clase Usuario directamente, pero supongamos que por cualquier razón eso no es posible. Otra solución seria crear una subclase de Usuario y añadir la funcionalidad allí. Finalmente, otra opción es crear un decorador:

DEFINE CLASS Usuario2 AS Custom
 HIDDEN oUsuario
 Login = ""
 Password = ""
 nombreCompleto = ""
 
 PROCEDURE Init  && Al crear la clase se crea una instancia de la clase real
  THIS.oUsuario = CREATE("Usuario")
 ENDPROC
 
 * Se programan Setters y Getters para cada propiedad de la clase real
 PROCEDURE Login_Access
  RETURN THIS.oUsuario.Login
 ENDPROC
 PROCEDURE Login_Assign(vNewVal)
  THIS.oUsuario.Login = m.vNewVal
 ENDPROC
 PROCEDURE Passsword_Access
  RETURN THIS.oUsuario.Password
 ENDPROC
 PROCEDURE Password_Assign(vNewVal)
  THIS.oUsuario.Password = m.vNewVal
 ENDPROC
 PROCEDURE nombreCompleto_Access
  RETURN THIS.oUsuario.nombreCompleto
 ENDPROC
 PROCEDURE nombreCompleto_Assign(vNewVal)
  THIS.oUsuario.nombreCompleto = m.vNewVal
 ENDPROC 
 
 * Se implementan todos los metodos de la clase real, invocando el metodo directamente en la instancia de la clase
 PROCEDURE verificarPassword(pcPwd)
  RETURN THIS.oUsuario.verificarPassword(pcPwd)
 ENDPROC
 PROCEDURE Login
  RETURN THIS.oUsuario.Login
 ENDPROC
 PROCEDURE Logout
  RETURN THIS.oUsuario.Logout
 ENDPROC
 
 * Se implementan los nuevos metodos que extienden la funcionalidad de la clase original
 PROCEDURE cambiarPassword(pcOldPwd, pcNewPwd)
 
ENDDEFINE

Una vez definido, podemos usar la clase Usuario2 en cualquier lugar donde antes se usaria Usuario:

LOCAL oUsuario
oUsuario = CREATE("Usuario2")
 
oUsuario.Login()    && Metodo en la clase Usuario
oUsuario.cambiarPassword(cOld, cNew)   && Método en la clase Usuario2


Como vemos, el decorador cumple dos funciones básicas:

  • Implementa toda la interfaz de la clase que se esta decorando
  • Implementa los nuevos metodos y propiedades que extienden la funcionalidad de la clase original


Tambien es obvio con este ejemplo que crear un decorador de una clase con una interfaz grande puede llegar a ser un trabajo tedioso. Justamente esta ultima parte es lo que me habia mantenido alejado del uso de decoradores.... hasta ahora.

La clase DecoratorPattern

La clase DecoratorPattern permite implementar un decorador de una clase cualquiera sin necesidad de recrear la interfaz del objeto que se desea decorar. En su lugar, la clase hace uso de las capacidades de reflexión de VFP y de la posibilidad en VFP de crear un accesor sobre la propiedad THIS para lograr el mismo efecto sin necesidad de escribir código. Utilizando la clase DecoratorPattern, la definición de Usuario2 se reduce a esto:

DEFINE CLASS Usuario2 AS DecoratorPattern
 className = "Usuario"
 
 * Se implementan los nuevos metodos que extienden la funcionalidad de la clase original
 PROCEDURE cambiarPassword(pcOldPwd, pcNewPwd)
ENDDEFINE

Aunque la declaración es sustancialmente distinta y mas sencilla que la anterior, su uso continua siendo el mismo:

LOCAL oUsuario
oUsuario = CREATE("Usuario2")
 
oUsuario.Login()    && Metodo en la clase Usuario
oUsuario.cambiarPassword(cOld, cNew)   && Método en la clase Usuario2


La clase DecoratorPattern funciona creando una instancia de la clase indicada en la propiedad className durante la inicialización de la instancia de la clase decorator y luego redirecciona hacia esa instancia las llamadas a miembros no implementados directamente por el decorador, de modo que cuando se invoca una propiedad o método definido en la clase original, la clase pasa el mensaje a la instancia de la clase original que se mantiene almacenada dentro de una propiedad hidden en la clase decoradora; si por el contrario se invoca un propiedad o método definido en la clase decoradora, entonces la clase asume el control y procesa el mensaje.


Encadenando decoradores

Para mostrar la forma en que la clase DecoratorPattern permite encadenar dos o mas decoradores, utilizaremos el famoso ejemplo de la rana. Supongamos que tenemos una clase que representa a una rana:

DEFINE CLASS Rana AS Custom
 PROCEDURE Come
 PROCEDURE Salta
ENDDEFINE

Como vemos, hemos definido una rana que puede comer y saltar. Digamos que ahora queremos crear una rana que, ademas de comer y saltar, tambien pueda cantar:

DEFINE CLASS ranaCantora AS DecoratorPattern
 className = "Rana"
 PROCEDURE Canta
ENDDEFINE

Pero no paremos aqui. Digamos que ahora queremos crear una rana que pueda Correr:

DEFINE CLASS ranaCorredora AS DecoratorPattern
 className = "Rana"
 PROCEDURE Corre
ENDDEFINE

Veamos como funcionan estos decoradores por separado:

LOCAL oRana
oRana = CREATE("Rana")
oRana.Come()  
oRana.Salta() 
 
LOCAL oRanaCantora
oRanaCantora = CREATE("ranaCantora")
oRana.Come()    && Implementado por la clase Rana
oRana.Salta()   && Implementado por la clase ranaCantora
 
LOCAL oRanaCorredora
oRanaCorredora = CREATE("ranaCorredora")
oRana.Salta()   && Implementado por la clase Rana
oRana.Corre()   && Implementado por la clase ranaCorredora

Y si ahora quisiéramos tener una rana que cante y corra ? Podemos lograrlos simplemente encadenando los decoradores:

LOCAL oSuperRana
oSuperRana = CREATE("ranaCantora", CREATE("ranaCorredora"))
oSuperRana.Come()   && Implementado por la clase Rana
oSuperRana.Canta()  && Implementado por la clase ranaCantora
oSuperRana.Corre()  && Implementado por la clase ranaCorredora

El truco esta en que la clase DecoratorPattern recibe un parámetro opcional en su método constructor (Init). Este parámetro contiene la instancia de la clase que se desea decorar. Si no se indica (como en los ejemplos anteriores), la clase usa el valor de la propiedad className para crear una instancia de dicha clase automáticamente. Esto permite aplicar funcionalidad adicional a una clase en forma aditiva, es decir, segun lo que se necesite (lo cual es una de las ventajas de esta tecnica sobre el uso de subclases).

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox