Implementacion de interfaces en VFP

From codeWiki
Jump to: navigation, search


Por VictorEspina

Contents

Introducción

Las interfaces son una tecnica muy utilizada en OOP para extender la funcionalidad de una clase sin hacer uso de la herencia, asi como para determinar en tiempo de ejecución si un objeto dado puede interactuar con otro objeto desconocido, determinando si dicho objeto implementa una interfaz conocida.

En términos generales, una interfaz define una serie de propiedades y metodos que una clase debe implementar para cumplir con dicha interfaz. Esto permite a objetos distintos interactuar de forna segura entre ellos sin necesidad de conocer completamente sus interfaces sino SOLO si implementan una interfaz común.

Visual FoxPro no soporta la declaración y uso de interfaces de forma nativa. Este articulo muestra una forma de implementar de forma parcial las bodandes del uso de interfaces en VFP 6.0 o superior, mediante el uso de una clase helper.


Clase InterfaceHelper

La clase InterfaceHelper ofrece una forma de declarar interfaces de forma sencilla (aunque limitada, ya que no es posible indicar la firma de los metodos, es decir, la lista de parámetros que espera cada metodo y el valor devuelto) y determinar si un objeto dado implementa una interfaz previamente declarada.

Supongamos que tenemos la siguiente clase:

DEFINE CLASS Usuarios AS Custom
ENDDEFINE

En este punto, no tenemos forma de saber si una instancia de esa clase contiene "n" elementos ni como podemos recorrer dichos elementos. Es aqui donde entran las interfaces. Supongamos que declaramos una interfaz llamada IEnumerable que define las propiedades y metodos que debe implementar un objeto para poder recorrer su contenido:

PUBLIC goInterfaces
goInterfaces = CREATE("interfaceHelper")
goInterfaces.Declare("IEnumerable","count,items,@indexOf")

Lo que estamos diciendo aca es que existe una interfaz llamada IEnumerable que consiste de las propiedades Count y Items, asi como el metodo indexOf. Si ahora hacemos:

LOCAL oUsuarios
oUsuarios = CREATE("Usuarios")
?goInterfaces.Implements(oUsuarios, "IEnumerable") --> .F.

Podremos ver que la clase Usuarios no ofrece una forma de recorrer su contenido, ya que no implementa la interfaz IEnumerable. Ahora, solucionemos ese problema:

DEFINE CLASS Usuarios AS Custom
 Count = 0
 DIMEN Items[1]
 PROCEDURE indexOf(pcElement)
   ...
 ENDPROC
ENDDEFINE

Como se ve, esta nueva version de la clase Usuarios implementa las propiedades y metodos indicados en IEnumerable, por lo que ahora podemos hacer esto sabiendo de antemano que no obtendremos un error en tiempo de ejecución:

LOCAL oUsuarios
oUsuarios = CREATE("Usuarios")
IF goInterfaces.Implements(oUsuarios, "IEnumerable")
 FOR i = 1 TO oUsuarios.Count
  oUsuario = oUsuarios.Items[i] 
 ENDFOR
ENDIF


Las ventajas de esto pueden aplicarse a todo tipo de objetos. Supongamos que queremos determinar si un objeto dado puede recibir o no el foco:

goInterfaces.Declare("IFocusable","@setFocus")
 
IF goInterfaces.Implements(oObject, "IFocusable")
 oObject.setFocus()
ENDIF


Caso especial para VFP 9

Si se esta usando VFP 9, la clase incluye un constructor adicional para la clase InterfaceInfo que permite definir una interfaz a partir de una clase. Por ejemplo, en lugar de declarar la interfaz IEnumerable manualmente, tal como vimos en los ejemplos anteriores, crearíamos una clase que herede de la clase especial Interface:

DEFINE CLASS IEnumerable AS Interface
 Count = 0
 DIMEN Items[1]
ENDDEFINE

Y luego, declaramos la interfaz en el helper usando el mismo metodo Declare:

goInterfaces.Declare("IEnumerable")

Como vemos, ya no seria necesario indicar la firma de la inrterfaz ya que la misma se obtendra directamente de la clase.

Codigo Fuente

* INTERFACEHELPER.PRG
* Clase para soporte parcial de interaces en VFP 6.0 o superior
*
* Autor: Victor Espina
* Fecha: Abril 2012
*
*
DEFINE CLASS interfaceHelper AS Custom
 *
 DIMEN Interfaces[1]
 interfaceCount = 0
 
 * Constructor 
 PROCEDURE Init
  DIMEN THIS.Interfaces[1]
  THIS.interfaceCount = 0
 ENDPROC
 
 * Declare
 * Permite declarar una interfaz. Una interfaz se declara
 * indicando su nombre y su "firma", la cual consiste en una
 * lista de propiedades y metodos que deben implementar las
 * clases para ser compatibles con la interfaz.
 *
 * ej:
 * oInterfaces.Declare("IEnumerable","count,items,@indexOf")
 *
 * Los metodos se deben identificar con el prefijo @. 
 *
 * NOTA PARA VFP 9:
 * Si se esta usando VFP 9, es posible declarar una interfaz
 * a partir de una clase existente de la siguiente forma:
 *
 * oInterfaces.Declare("className")
 *
 PROCEDURE Declare(pcName, pcSignature)
  LOCAL oInterface
  oInterface = CREATE("interfaceInfo")
  oInterface.Name = pcName
  IF VARTYPE(pcSignature) = "C"
   oInterface.initWithSignature(pcSignature)
  ELSE
   oInterface.initWithClass(pcName)
  ENDIF
  THIS.interfaceCount = THIS.interfaceCount + 1
  DIMEN THIS.Interfaces[THIS.interfaceCount]
  THIS.Interfaces[THIS.interfaceCount] = oInterface
 ENDPROC


 * indexOf
 * Devuelve la posicion de una interfaz dada dentro de la coleccion
 * de interfaces o CERO si la interfaz no esta declarada.
 PROCEDURE indexOf(pcInterface)
  LOCAL i,nIndex
  nIndex = 0
  pcInterface = LOWER(pcInterface)
  FOR i = 1 TO THIS.interfaceCount
   IF LOWER(THIS.Interfaces[i].Name) == pcInterface
    nIndex = i
    EXIT
   ENDIF
  ENDFOR
  RETURN nIndex
 ENDPROC 
 
 
 * Accesor para la propiedad Interfaces. Esto permite
 * acceder a la coleccion de interfaces por posicion
 * o por nombre de la interfaz
 PROCEDURE Interfaces_Access(nIndex1, nIndex2)
  IF VARTYPE(nIndex1) = "N"
   RETURN THIS.Interfaces[nIndex1]
  ENDIF
  LOCAL nIndex
  nIndex = THIS.indexOf(nIndex1)
  IF nIndex > 0
   RETURN THIS.Interfaces[nIndex]
  ENDIF
 ENDPROC
 
 
 * Implements
 * Permite determinar si una instancia dada implementa
 * una interfaz especifica
 PROCEDURE Implements(poObjectRef, pcInterface)
  LOCAL nIndex,oInterface
  nIndex = THIS.IndexOf(pcInterface)
  IF nIndex = 0
   RETURN .F.
  ENDIF
  
  LOCAL lResult,i,cMember,cType,cMemberType,lIsMember
  oInterface = THIS.Interfaces[nIndex]
  lResult = .T.
  FOR i = 1 TO oInterface.memberCount
   cMember = oInterface.Members[i, 1]
   cType = oInterface.Members[i, 2]   
   IF THIS.getMemberType(poObjectRef, cMember) <> cType
    lResult = .F.
    EXIT
   ENDIF
  ENDFOR
  
  RETURN lResult
 ENDPROC
 
 
 * getMemberType
 HIDDEN PROCEDURE getMemberType(poObjectRef, pcMember)
  DO CASE
     CASE NOT PEMSTATUS(poObjectRef, pcMember, 5)
          RETURN "None"
          
     CASE TYPE("poObjectRef." + pcMember) = "U"
          RETURN "Method"
          
     OTHERWISE
          RETURN "Property"
  ENDCASE
 ENDPROC
 *
ENDDEFINE


* interfaceInfo (Class)
* Clase que representa los datos de una interfaz declarada
*
DEFINE CLASS interfaceInfo AS Custom
 Signature = ""
 DIMEN Members[1,1]
 memberCount = 0
 
 * initWithSignature
 * Permite inicializar la interfaz con un string de firma que
 * contenga las propiedades y metodos que componen la interfaz
 PROCEDURE initWithSignature(pcSignature)
  LOCAL ARRAY laMembers[1]
  LOCAL nCount, i, cMember
  nCount = ALINES(laMembers, STRT(pcSignature,",",CHR(13)+CHR(10)))
  THIS.memberCount = nCount
  DIMEN THIS.Members[MAX(1, THIS.memberCount),2]
  FOR i = 1 TO nCount
   cMember = laMembers[i]
   IF LEFT(cMember,1) = "@"
    THIS.Members[i,1] = SUBS(cMember, 2)
    THIS.Members[i,2] = "Method"
   ELSE
    THIS.Members[i,1] = cMember
    THIS.Members[i,2] = "Property"
   ENDIF 
  ENDFOR
  THIS.Signature = pcSignature
 ENDPROC
 
 #IF "09.00" $ VERSION()
 * initWithClass
 * Permite definir el contenido de la interfaz a partir
 * de una clase dada
 PROCEDURE initWithClass(pcClassName)
  LOCAL oInstance
  oInstance = CREATEOBJECT(pcClassName)
  THIS.memberCount = AMEMBERS(THIS.Members, oInstance, 1, "U")  
 ENDPROC
 #ENDIF
ENDDEFINE


* Interface (Class)
* Clase base para la declaracion de interfaces en VFP 9
*
DEFINE CLASS Interface AS Custom
ENDDEFINE
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox