Objects y la Herencia Multiple, como?

Post Reply
User avatar
RenOmaS
Posts: 205
Joined: Fri Oct 07, 2005 5:07 pm

Objects y la Herencia Multiple, como?

Post by RenOmaS »

Despues de revisar los fuentes de harbour referente a objects y no teniendo la capacidad para poder entender. recurro a uds. si es que alguien me puede orientar sobre la herencia multiple.

Tengo este codigo

Code: Select all

CLASS TData FROM TDataBase, TRs, TMySql
   CLASSDATA cParent INIT 'TDATABASE'   
ENDCLASS

CLASS TRs
   METHOD New()
   METHOD Open()
   ...(etc)
ENDCLASS

CLASS TMySql
   METHOD New()
   METHOD Open()
   ...(etc)
ENDCLASS
Lo que quiero hacer es que la clase TData se comporte de acuerdo a cierta circunsancia como la clase padre TDatabase o la clase TRs o la clase TMysql, esto lo defino en la classdata cParent.

Siguiendo el Ng de Objects de Clipper, sobre herencia multiple, he tratado de hacer esto:

Code: Select all

CLASS TData FROM TDataBase, TRs, TMySql

   CLASSDATA cParent INIT 'TDATABASE'   

   METHOD New()          INLINE Super( ::cParent ):New()
   // o tambien
   METHOD Open()         INLINE ::Super( ::cParent ):Open()
   ...(etc)
ENDCLASS
Pero tengo errores al quere compilar.

Es posible hacer esto?.
O como se puede hacer para que una clase hija se comporte como una determinada clase padre cuando se tiene herencia multiple.

Saludos y gracias
User avatar
quique
Posts: 408
Joined: Sun Aug 13, 2006 5:38 am
Contact:

Post by quique »

De acuerdo a mi experiencia, la super hace referencia a la clase padre principal, que generalmente debería ser la primera de la lista, aunque de repente no me ha funcionado correctamente y toma la última.

Super se maneja como variable de instancia, no como método, por ejemplo

method new inline super:new()

sería equivalente a

method new inline ::tDatabase:new()

En cada objeto se crea una variable de instancia como referencia a cada clase padre que tienes, en este caso, por ejemplo, podrías hacer algo parecido a lo siguiente

Code: Select all

method new( cParent ) class tData
   DO CASE
      CASE upper( cParent ) == "TDATABASE"
         ::tDatabase:new()
         // como tdatabase es la clase padre principal, también podría ser
         // super:new()
      CASE upper( cParent ) == "TRS"
         ::tRs:new()
      CASE upper( cParent ) == "TMYSQL"
         ::tMySql:new()
   ENDCASE
return self
Espero haberte orientado un poco, o por lo menos confundido mas :P
Saludos
Quique
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post by Antonio Linares »

La herencia múltiple es un concepto que a la hora de la práctica genera más problemas que soluciones.

Lo más sencillo es crear una clase "contenedora", es decir una clase que "contiene" objetos y que los administra:

Code: Select all

CLASS TDATA

   DATA oData

   METHOD New( cType, ... )
   METHOD Open( ... ) INLINE ::oData:Open( ... )
   METHOD Close() INLINE ::oData:Close()
   ...
ENDCLASS

Code: Select all

METHOD New( cType, ... ) CLASS TData

   do case
        case cType == "database"
                ::oData = TDataBase():New( ... )

        case cType == "rs"
                ::oData = TRS():New( ... )

        case cType == "mysql"
                ::oData = TMySQL():New( ... )

        etc...
    endcase

return Self
Asi un objeto de la Clase TData es un "interface" que permite actuar sobre distintos tipos de objetos según sean las circunstancias. Y además puede contener tantos objetos como hagan falta.
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
RenOmaS
Posts: 205
Joined: Fri Oct 07, 2005 5:07 pm

Post by RenOmaS »

Gracias por responder.
Lo que indican ya lo tengo implementando de esa forma.
Pero uno siempre busca hacer las cosas mas dificiles :D

Pero estudiando mas los objetos existe una funcion __objModMethod( oObject, cSymbol, nFuncPrt ) que reemplaza los punteros de las funciones de los methodos..

se podria utilizar el siguiente codigo para poder simular la herencia multiple

Code: Select all

CLASS TData FROM TDataBase
   CLASSDATA cParent INIT 'TDATABASE'   
ENDCLASS

CLASS TRs
   METHOD New()
   METHOD Open()
   ...(etc)
ENDCLASS

CLASS TMySql
   METHOD New()
   METHOD Open()
   ...(etc)
ENDCLASS 


Function Main() 
  Local oData := TData():New()
  ....
  AhoraEresMYsql( oData )

Return Nil

Static Function AhoraEresMYSql( oData )
   Local i
   //..leo los metodos..
   Local aMet := __objGetMethodList( oData )
  // despues por cada uno los metodos hacer
  For i := 1 To Len( aMet )
        __objModMethod( oData, aMet[ i ], nFuncPrt )
  EndFor

Return Nil

Ahora la pregunta es.. existe alguna funcion.. que me devuelva el puntero de la funcion asociada a un metodo ??

Gracias
User avatar
quique
Posts: 408
Joined: Sun Aug 13, 2006 5:38 am
Contact:

Post by quique »

Aunque no comparto la idea de antonio en que la herencia multiple genera mas problemas que soluciones, si estoy de acuerdo en que muchas veces es mejor manejar clases contenedoras como lo dice antonio, como por ejemplo, lo es tu caso. No creo que sea buena idea tener que estar modificando la clase de esa manera para simular la herencia multiple, lo mas seguro es que en tu caso no tengas problemas, porque no creo que utilices mas de un tipo de clase (herencia) en el programa ¿pero que pasa si tuvieras que utilizar 2 o 3?

Por otro lado, yo utilizo eso que haces para modificar las clases de fivewin, por ejemplo, no se si has visto el editor que estoy haciendo, maneja barra de mensajes y pestañas para indicar los archivos abiertos, ambos como barras inferiores, es mas, si abres demasiados archivos puedes tener 2 o mas barras de tabs, algo que no permite de manera nativa fivewin ¿pero cual es el problema? bueno, pues que lo que hice fue modificar algunos métodos y variables de instancia, no solo para eso que comento, sino para otras operaciones del programa, con las funciones que indicas, y si tengo algún problema, por ejemplo un mensaje de error al inicio del programa, o por hacer pruebas creo una ventana antes de la ventana principal, me causa errores como si no funcionara bien el llamado a las funciones para modificar las clases.

No se a que se deba, y claro, el tipo de clases que modifico (window, control, y una que otra mas de algun control en especifico) son totalmente distintas a la que estas modificando tu, pero supongo que podrías llegar a tener algún tipo de complicación como la que me ocurre a mi.

En tu caso, te recomendaría algo mas parecido a lo siguiente (si es que utilizas xharbour, con harbour tendrías que manejar (...) como en el ejemplo de antonio).

Code: Select all

CLASS tData FROM TDataBase
   data oDatabase
   method new
   error handler onError
ENDCLASS

METHOD new( ... ) CLASS tData
   local aParams := hb_aParams()
   local cType   := aParams[ 1 ]
   aDel( aParams, 1, .t. )

   do case 
        case cType == "database" 
                ::oData = TDataBase():New( aParams ) 

        case cType == "rs" 
                ::oData = TRS():New( aParams ) 

        case cType == "mysql" 
                ::oData = TMySQL():New( aParams ) 

    endcase 

return self 

METHOD onError( ... ) CLASS TData
return hb_execFromArray( oDatabase, __getMessage(), hb_aParams() )
De hecho, este ejemplo que te estoy dando realmente lo uso, no para manejar archivos, sino en clases que manejan browse, y también tengo algo parecido a lo que quieres manejar, por ejemplo, tengo estos dos comandos

DEFINE WINDOW BROWSE oWnd ARRAY aDatos HEAD "col1", "col2", "col3"

y

DEFINE WINDOW XBROWSE oWnd ARRAY aDatos HEAD "col1", "col2", "col3" AUTOCOLS

con esas dos creo dos ventanas con un browse, uno de la clase qWBrowse() (hereda de tWbrowse() ) y el otro de qXBrowse() (hereda de tXBrowse() ), ambas heredan de las mismas clases, pero manejan un browse distinto, y en cada una cargo el browse correspondiente, por ejemplo, eso quiere decir que si en algun problema utilizo el segundo comando y no el primero, no se carga en memoria twbrowse porque no hago referencia en ningún lado a ese fuente, pero con solamente escribir

oWnd:goTop()

le estoy diciendo al browse que se vaya al inicio del archivo.

Y en estas clases que te comento hago uso de ambos metodos, tanto uso herencias multiples, como clase contenedora, y la clase contenida es el browse, al cual tengo accesos con el capturador de error como te lo muestro en el ejemplo.
Saludos
Quique
User avatar
Ricardo Ramirez E.
Posts: 161
Joined: Wed Jan 25, 2006 10:45 am
Location: Praia - Cape Verde
Contact:

Post by Ricardo Ramirez E. »

Estuve siguiendo este hilo y a espera de respuestas :)
Los ejemplos de Antonio (concreto) y de Quique (mas detallado) son interesantes... :)

Renomas, será conveniente manipular los punteros a metodos?
navegando encontré esto....

http://gp32spain.com/foros/showthread.p ... 432&page=2
http://arco.esi.uclm.es/~david.villa/pe ... 11s04.html

Saludo.
Saludos
Ricardo R.
xHarbour 1.1.0 Simplex , Microsoft Visual Studio 2008, Bcc55, Fwh Build. 9.01
Carlos Mora
Posts: 988
Joined: Thu Nov 24, 2005 3:01 pm
Location: Madrid, España

Post by Carlos Mora »

Hola Renomas,

es interesante el tema que estas atacando, aunque seria mejor hacerlo de una forma diferente.
Tal vez lo mejor es definir una clase ABSTRACTA, es decir una clase que solo implementa una interfaz con métodos virtuales, y luego usando la herencia se implementan las subclases. Ese sería el mejor modelo técnicamente, en Delphi eso se llama DataSets, tambien en .Net.
Es decir que tendrías una clase DataSet, con poco código, y luedo tu DataBase, TMySQL, TADO y todo lo demás hereda de ella.
Lo unico que cambiaría es que en lugar de hacer

oData:= TData():New( 'MySQL' )

Harás

oData:= TMySQL():New()

Y oData lo podrás usar de igual manera en todos lados porque tendrá los mismos métodos sin importar el tipo del que sea.

Tambien tiene la ventaja que que solo se enlaza las clases que realmente usas. Con el sistema que propones, no importa si usas o no ADO o MySQL, se enlazarán siempre porque las funciones de esos manejadores son invocadas por tu TData.

Espero haberme esplicado bien. Este tema ya lo discutimos en su momento con TheFull hace un tiempo en el grupo Olivares, y esta fue la conclusion a la que llegamos.

Te he añadido al msn,

Un saludo,
Carlos.
User avatar
RenOmaS
Posts: 205
Joined: Fri Oct 07, 2005 5:07 pm

Post by RenOmaS »

Quique:
El fin de esto seria poder utilizar indistintivamente como se debe de comportar Tdata de acuerdo al requeremiento del usuario (al momento de instalar poder seleccionar con que B.D. quiere trabajar) y de la aplicacion que se pretenda hacer( poder seleccionar datos de diferente motores ).

Es interesante lo que estas haciendo, hace un tiempo estuve intentando hacer algo parecido, lo tengo en los pendientes, voy a testar mas tus ejemplos.

Ricardo:
Informacion interesante, aunque la implementacion de objects en harbour creo que es un poco diferente.

Carlos:
Se entiende lo que explicas y es interesante, solo que puede tener una pega.
Generalment en mi caso utilizo objetos que heredan de TData, es decir por ejemplo tengo TClientes, TFacturas que heredan de TData, como podras deducir tienes su datas y metodos propios y los que hereda de TData, que a su vez hereda de la TDataBase de FW, y lo que se pretende es que TData pueda heredar de mas de una clase, para que el comportamiento de la misma pueda modificarse dependiendo de cual clase padre esta herendando.

Creo que tienen que enlazarse todas las clases envolvidas, porque se debe de pretender que es el implementador del sistema el que define con cual B.D. va a querer trabajar.

.....

Siguiendo con los objectos
En objects al momento de crear la clase, y si esta tiene herencia, se agrega las datas y metodos de la clases padre, claro que solo agrega aquellas que no estan definidas en la clase hija, esto lo hace comenzando desde la primera clase padre hasta el ultimo, podemos ver esto la funcion hb_clsNew(), por eso que deberia tomar el metodo de la 1ra clase padre.

Para poder ultrapasar esto, estoy intentando implementar lo siguiente.

Code: Select all

_HB_CLASS TData
function TData ( ... )
   static s_oClass
   local nScope, oInstance
   if s_oClass == NIL
      nScope := 1
      Do Case
         Case cType == 'DATABASE'  // cType seria una variable global
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TDataBase() }, { @HBObject() } ), @TData() ) ) ;

         Case cType == 'RS'
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TRs() }, { @HBObject() } ), @TData() ) ) ;

         Case cType == 'MYSQL'
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TMysql() }, { @HBObject() } ), @TData() ) ) ;
      EndCase

   METHOD New()          INLINE Super():New()

   METHOD Open()         INLINE Super():Open()

   ...

ENCLASS
Lo que se pretende en final es querer tener objetos (que manipulen los datos indpendientemente de cual es RDD que estemos utilizando o el modo de que nos hemos conectado a la Base de Dados) que hereden de TData.
TData que seria la clase Abstracta, o puente, o como querramos llamarla.
Y asu vez TData se comportaria como TDataBase, o como TRs o como TMysql, ou outro que apareciera.

Saludos
User avatar
quique
Posts: 408
Joined: Sun Aug 13, 2006 5:38 am
Contact:

Post by quique »

RenOmaS, con el ejemplo que te puse creo que es suficiente para hacer lo que necesitas, o sea, que según yo, con esa clase no necesitas mas a menos que desees modificar algo. No estoy familiarizado con las clases que quieres utilizar, así que los ejemplos que te daré son suposiciones que tendrías que

Lo que puedes hacer es

oDbf := tData( "database" )
oMySql := tData( "mysql", par1, par2, parN )
oTrs:= tData ( "rs", par1, par2, parN )

con eso creas los tres objetos y ya puedes trabajar

oDbf:goTop()
oMySql:dbSeek( oTrs:factura )

ahora bien, si hubiera metodos que trabajan de manera difrente, solamente tendrías que hacer las respectivas modificaciones, por ejemplo

Code: Select all

#define TIPO_DATABASE   1
#define TIPO_MYSQL      2
#define TIPO_RS         3

CLASS tData FROM TDataBase 
   data nTipo
   data oDatabase 
   method new 
   method dbSeek
   error handler onError 
ENDCLASS 

METHOD new( ... ) CLASS tData 
   local aParams := hb_aParams() 
   local cType   := aParams[ 1 ] 
   aDel( aParams, 1, .t. ) 

   do case 
      case cType == "database" 
         ::oData := TDataBase():New( aParams ) 
         ::nTipo  := 1

      case cType == "rs" 
         ::oData = TRS():New( aParams ) 
         ::nTipo  := 2

      case cType == "mysql" 
         ::oData = TMySQL():New( aParams ) 
         ::nTipo  := 3

   endcase 

return self 

method dbSeek( xSeek ) class tData
   local lRet := .f.
   switch ::nTipo
      case TIPO_DATABASE
         lRet := ::oData:dbSeek( xSeek )
         exit
      case TIPO_MYSQL
         lRet := ::oData:seek( xSeek )
         exit
      case TIPO_RS
         lRet := ::oData:buscar( xSeek )
         exit
   end switch
return lRet

METHOD onError( ... ) CLASS TData 
return hb_execFromArray( oDatabase, __getMessage(), hb_aParams() )
Saludos
Quique
User avatar
RenOmaS
Posts: 205
Joined: Fri Oct 07, 2005 5:07 pm

Post by RenOmaS »

Quique..
Se tiene este caso....

Code: Select all

                          +---------+
                          |TDataBase|
                          |         |
                          +---+-----+
                              |
                          +---------+
                          |  TData  |
                          |         |
                          +---+-----+
                              |
         +------------+-------+------+-------------+
         |            |              |             |
    +----+----+   +---+-----+   +----+----+   +---------+
    | TCientes|   |TFacturas|   | TRecibos|   |  T...   |
    |         |   |         |   |         |   |         |
    +---------+   +---------+   +---------+   +---------+
Como comprenderas las classes hijas heredan de datas y metodos de la clase padre y tambien tienen data y metodos propios.
(ojo que, en este caso, la TDataBase no es la original de FW, tiene sus agregados, datas y metodos mas o menos complejos)
Ademas las clases hijas son en lo posible abtractas, e decir, no hacen referencia a modo de acceder a los datos (alias, area, field, etc.).

Lo que se pretende es querer tener la posibilidad de trabajar con otros modos de acceder a datos ( llamense RecordSet, mysql, postgres, etc.), y esto (como siempre) tocar lo menos posible el codigo.
vamos, querer que todo funcione igual, sin importar la forma en que estoy accediendo a los datos, lo que los "objetistas puros" llamam abstracion.

La forma que sugieres es crear una clase contenedora (como tambien lo ha explicado muy bien Antonio). Pero esto implicaria tener, en este caso, modificar (a mi parecer) mucho codigo. Te explico, por ejemplo se puede tener un metodo Create en TData, esto se utilizaria, por ejemplo para poder crear la tabla en momento de ejecucion, utilizamos la data 'cFileName', que es una classdata de las hijas, indica el nombre del archivo a crear ( 'CLIENTES', 'FACTURAS', 'RECIBOS'), por ejemplo podemos tener:.

Code: Select all

METHOD Create() CLASS TData  CONSTRUCTOR

   Super:Create( ::cFileName, ::Structure(), ::cDriver )
   // ::Structure() es um metodo, que me devolvera la estructura que debe de tener la tabla, no confundir con ::DbStructure() que devueve la estrutura fisica existente.
   Return Self

Utlizando la sugerencia tendriamos (ojo de el METHOD Create es Contructor), es decir aun no sabemos como estamos acceder a los datos.

Code: Select all

Method Create( nTipo ) class tData
   local lRet := .f.
   Do Case 
      case nTipo == TIPO_DATABASE
         ::oData := TDataBase():Create( ::cFileName, ::Structure(), ::cDriver )
         exit
      case nTipo == TIPO_MYSQL
         ::oData := TMySql():Create( ::cFileName, ::Structure(), ::cDriver )
         exit
      case nTipo == TIPO_RS
         ::oData := TRs():Create( ::cFileName, ::Structure(), ::cDriver )
         exit
   EnCase
return ???   ( ::oData o Self  ver cual es lo mejor )
Asi, por estilo tendriamos que modificar unos metodos, y enviar o establecer el tipo de acceso de datos.

Pero definiendo en un inicio como se comportaria TData mediante esto:

Code: Select all

...
      Do Case
         Case cType == 'DATABASE'  // cType seria una variable global
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TDataBase() }, { @HBObject() } ), @TData() ) ) ;

         Case cType == 'RS'
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TRs() }, { @HBObject() } ), @TData() ) ) ;

         Case cType == 'MYSQL'
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TMysql() }, { @HBObject() } ), @TData() ) ) ;
   ...
   ...
   METHOD New(...)         INLINE Super:New(...)
   METHOD Create(...)      INLINE Super:Create(...)
   METHOD Seek(...)        INLINE Super:Seek(...)
   ...
 
Creo que no se modificaria poco codigo. Es solo indicar el Super:[Method] y es esa clase la encaragaria de eso.
entonces se tendria esto de aqui:

Code: Select all

                          +---------+
                          | TData   |
                          |  Sets   |
                          +---+-----+
                              |
         +------------+-------+------+-------------+
         |            |              |             |
    +----+----+   +---+-----+   +----+----+   +---------+
    | TDatBase|   |   TRs   |   | TMYSql  |   |  T...   |
    |         |   |         |   |         |   |         |
    +----+----+   +---+-----+   +----+----+   +---------+
         |            |              |             |
         +------------+--------+-----+-------------+
                               |
                          +----+----+
                          |  TData  |
                          |         |
                          +----+----+
                               |
         +------------+--------+-----+-------------+
         |            |              |             |
    +----+----+   +---+-----+   +----+----+   +---------+
    | TCientes|   |TFacturas|   | TRecibos|   |  T...   |
    |         |   |         |   |         |   |         |
    +---------+   +---------+   +---------+   +---------+
Esto creo que esta bien si es que queremos utilizar un mismo modo de acceder a los datos, es decir se accede mediante Mysql y nada mas, y el manejo de los datos se hacen mediante cursores, etc., lo que tenga mysql..

Pero, que sucede si queremos acceder tanto como DataBase y como TMysql, por ejemplo, para poder utilizar los datos en forma local, es decir obtenemos los datos (mediante una consulta puede ser), y estos datos queremos tratarlos como una dbf comun..
Es este caso que tendriamos que utilizar un contenedor de datos, puede ser dentro de la TMysql un oData que se comporte como Dbf, o como um TOracle, si es que queremos trabajar como tal.

Uds. que opinan?

Gracias.
User avatar
quique
Posts: 408
Joined: Sun Aug 13, 2006 5:38 am
Contact:

Post by quique »

En mi caso, opino que una de 3, o no te entiendo o no me entiendes o hablamos de lo mismo pero no nos entendemos :P
Como comprenderas las classes hijas heredan de datas y metodos de la clase padre y tambien tienen data y metodos propios.
(ojo que, en este caso, la TDataBase no es la original de FW, tiene sus agregados, datas y metodos mas o menos complejos)
Ademas las clases hijas son en lo posible abtractas, e decir, no hacen referencia a modo de acceder a los datos (alias, area, field, etc.).

Lo que se pretende es querer tener la posibilidad de trabajar con otros modos de acceder a datos ( llamense RecordSet, mysql, postgres, etc.), y esto (como siempre) tocar lo menos posible el codigo.
Creo que esto es importante, pero como lo has dicho, es abstracto, entonces no nos preocupa como lo maneja la base de datos, con el ejemplo que te di no nos interesa como lo maneje la base de datos, tampoco nos interesa si lo que quieres es manejar métodos o variables, simplemente los llamas y la clase que te puse lo deberá interpretar, si tu defines que la clase tendrá ese método, pues será un método, si decides definir en la clase una variable, pues será una variable, si decides que la decisión la tome la clase manejadora de la base de datos, simplemente no se declara en la clase y listo. Como te puse en el ejemplo del dbSeek() cada manejador lo llama a su manera y eso lo puedes controlar tu en esta clase. No tendrás que modificar para nada el código fuente del programa si en la clase programas lo necesario para cada método que así lo requiera.

Code: Select all

Method Create( nTipo ) class tData 
   local lRet := .f. 
   Do Case 
      case nTipo == TIPO_DATABASE 
         ::oData := TDataBase():Create( ::cFileName, ::Structure(), ::cDriver ) 
         exit 
      case nTipo == TIPO_MYSQL 
         ::oData := TMySql():Create( ::cFileName, ::Structure(), ::cDriver ) 
         exit 
      case nTipo == TIPO_RS 
         ::oData := TRs():Create( ::cFileName, ::Structure(), ::cDriver ) 
         exit 
   EnCase 
return ???   ( ::oData o Self  ver cual es lo mejor )
mencionas que es constructor, todo método constructor debe devolver self, así que no hay duda de la respuesta. Por otro lado, supongo que tu pregunta va dirigida al uso que se tendría en la práctica. Respuesta: self. ¿Por que? porque el acceso al manejador de las bases de datos lo hará la clase, no tu en el nuevo programa, como comentas en el mensaje, el programa no debe saber como ni porque, solo trabaja con el objeto que creas y lo demas es una caja negra.

Code: Select all

_HB_CLASS TData 
function TData ( ... ) 
   static s_oClass 
   local nScope, oInstance 
   if s_oClass == NIL 
      nScope := 1 
      Do Case 
         Case cType == 'DATABASE'  // cType seria una variable global 
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TDataBase() }, { @HBObject() } ), @TData() ) ) ; 

         Case cType == 'RS' 
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TRs() }, { @HBObject() } ), @TData() ) ) ; 

         Case cType == 'MYSQL' 
              s_oClass := IIF(.F.,, HBClass():new( "TData" , iif( .T., { @TMysql() }, { @HBObject() } ), @TData() ) ) ; 
      EndCase 

   METHOD New()          INLINE Super():New() 

   METHOD Open()         INLINE Super():Open() 

   ... 

ENCLASS
Insisto en que no le veo caso a complicarte la vida tratando de heredar o adivinar que clases vas a utilizar, eso lo manejas definiendo el controlador en el método new(), o bueno, no te gusta ahí crea otro método para asignarlo y listo, pero no necesitas complicarte la vida.

Code: Select all

                          +---------+ 
                          | TData   | 
                          |  Sets   | 
                          +---+-----+ 
                              | 
         +------------+-------+------+-------------+ 
         |            |              |             | 
    +----+----+   +---+-----+   +----+----+   +---------+ 
    | TDatBase|   |   TRs   |   | TMYSql  |   |  T...   | 
    |         |   |         |   |         |   |         | 
    +----+----+   +---+-----+   +----+----+   +---------+ 
         |            |              |             | 
         +------------+--------+-----+-------------+ 
                               | 
                          +----+----+ 
                          |  TData  | 
                          |         | 
                          +----+----+ 
                               | 
         +------------+--------+-----+-------------+ 
         |            |              |             | 
    +----+----+   +---+-----+   +----+----+   +---------+ 
    | TCientes|   |TFacturas|   | TRecibos|   |  T...   | 
    |         |   |         |   |         |   |         | 
    +---------+   +---------+   +---------+   +---------+

1. según el último esquema tienes una clase (supongo que mas bien es el motor de la base que necesitas, pero pongamosla como ejemplo por la referencia del diagrama), aquí no harías nada porque la accesan directamente las clases hechas para administrar el motor

2. aquí entran las clases manejadoras del motor de base de datos que necesitas, las cuales hacen uso de tDataSets, tampoco harías nada porque supongo ya están hechas.

3. aquí encajaría el ejemplo que te puse, casi tal como está, y es aquí mismo donde tendrías que modificar los métodos para poder hacer las diferencias que necesitas para tomar las decisiones de las diferencias que necesitas para cada método, según entiendo, no necesitas herencia multiple, porque harías uso directo de la clase que necesitas, y es aquí, donde cada método tendría que ser modificado para poder indicarle a la clase manejadora como debe de funcionar, de hecho, creo que nisiquiera tendríamos que heredar esta clase de ninguna otra.

Code: Select all

CLASS tData
   data nTipo 
   data oDatabase 
   method new 
   method dbSeek 
   error handler onError 
ENDCLASS
4. finalmente, puedes crear cada una de las clases para cada tabla

Code: Select all

class tClientes from tData
endClass

class tFacturas from tData
endClass
Saludos
Quique
User avatar
RenOmaS
Posts: 205
Joined: Fri Oct 07, 2005 5:07 pm

Post by RenOmaS »

Hombre que si te entiendo.
Y mas aun te digo.. que tengo una TData modificada utilizada como contenedora.

Estoy a comparar las dos formas de implementar esto.
Insisto en que no le veo caso a complicarte la vida tratando de heredar o adivinar que clases vas a utilizar, eso lo manejas definiendo el controlador en el método new(), o bueno, no te gusta ahí crea otro método para asignarlo y listo, pero no necesitas complicarte la vida.
Es que no es complicacion ni adivinacion, es determinar al momento de crearse la clase TData como es que va a heredar... es como tener esto, pero con condiciones..
TData FROM TDataBase
o
TData FROM TMysql
o
TData FROM TRs.
y si aparecen otro mas, es solo adicionar otra condicion
TData FROM TOtroModo

Es decir con esto solo adicionaria. un para de lineas. y en enlazar TOtroModo.

Ya no tendria que modificar en los Metodos New(), Create(), o los que deberian ser modificados.
.

Siempre buscando la simplificacion.

Saludos.
User avatar
quique
Posts: 408
Joined: Sun Aug 13, 2006 5:38 am
Contact:

Post by quique »

de la manera que te digo es igual, solo tendrías que modificar la clase new con el case de la clase que quieres agregar. Pero lo bonito de la programación es que hay muchas formas de conseguir algo :)
Saludos
Quique
Post Reply