Apúntate...


Consultas, desarrollo de programas y petición de presupuestos:

sábado, 15 de septiembre de 2012

Mas Sobre Constructores y Hacer una Copia de un Objeto

Mas Sobre Constructores

 En el post anterior hablamos sobre el método especial "_new", que nos ayuda a dar valores a los parámetros o realizar instrucciones justo cuando instanciamos un objeto.


Los métodos también admiten (al igual que las funciones y las subrutinas), parámetros opcionales (optional). Además tenemos funciones en gambas que permiten conocer que tipo de parámetro es el que se le esta pasando al instanciar la clase.

Con esta dos herramientas, podemos programar que un mismo método _new, pueda crear objetos usando diversos tipos y números de parámetros.

Por ejemplo:

Tenemos la clase ClassCirculo, con la cual se crean Circulos.
Podemos definir los circulos de diversas maneras:
  • Forma 1:  x, y, radio, nos dan la X e Y y radio (integer)
  • Forma 2:  x, y, radio,  nos dan la X e Y y radio (pero son Float)
  • Forma 3:  classe Coordenada, radio (circulo dado el centro por la clase coordenada y su radio)
  • Forma 4: clase coordenda1,coordenado2,coordenada3 (circulo que pase por 3 puntos)
¿como se programa?
Lo que tenemos que hacer es usar varias ordenes If... then,,, para que segun sea el tipo de parametro pasar crearlo.

Para saber el tipo de parámetro, usamos:
  • La orden TypeOf(parametro1), nos devuelve un número que se identifica con el tipo de variable  (ver http://www.gambasdoc.org/help/lang/typeof?v3 )
  • Si sabemos que es un objeto  Object.Type(parametro1), que nos dá el nombre de la clase.

¿Como se defineria la clase ClassCirculo?
-->
Public Sub _new(Optional parametro1 As Variant, Optional parametro2 As Variant, Optional parametro3 As Variant)

' Forma: x, y, radio : nos dan la X e Y y radio (integer)

If TypeOf(parametro1) = gb.Integer And TypeOf(parametro2) = gb.Integer And TypeOf(parametro3) = gb.integer Then
Print "Me pasan 3 numeros integer, en la forma X, Y, Radio"
'realizo copia de propiedades...
'...
hx = parametro1
hy = parametro2
hradio = parametro3
estado = "iniciado"
Return
Else
' Forma: x, y, radio : nos dan la X e Y y radio (pero son Float)
If TypeOf(parametro1) = gb.float And TypeOf(parametro2) = gb.float And TypeOf(parametro3) = gb.float Then
Print "Me pasan 3 numeros float, en la forma X, Y, Radio"
'crearia el circulo
'...
hx = parametro1
hy = parametro2
hradio = parametro3
Return
Else

' forma: classe Coordenada, radio (circulo dado el centro por la clase coordenada y su radio)
If Object.Type(parametro1) = "ClassCoordenada" And TypeOf(parametro2) = gb.float Then
Print "Me pasan una clase ClassCoordenada, y el Radio (float)"
'crearia el circulo con los datos pasado
'....
'
Return
Else
' Forma: clase coordenda1,coordenado2,coordenada3 (circulo que pase por 3 puntos)
If Object.Type(parametro1) = "ClassCoordenada" And Object.Type(parametro2) = "ClassCoordenada" And Object.Type(parametro3) = "ClassCoordenada" Then
Print "Me pasan 3 clases ClassCoordenada"
'se crearia el circulo teniendo en cuenta 3 coordenadas.
'....
'
Return
Endif
Endif
Endif
Endif

¿como usar la clase?
-->
Print "================================================================"
Print "= Constructores ="
Print "================================================================"

' Forma: x, y, radio : nos dan la X e Y y radio (integer)
Print "Constructor caso:"
c1 = New ClassCirculo(2, 3, 4)
' Forma: x, y, radio : nos dan la X e Y y radio ( Float)
Print "Constructor caso:"
c2 = New ClassCirculo(2.3, 24.2, 34.2)

' forma: classe Coordenada, radio (circulo dado el centro por la clase coordenada y su radio (float))
Print "Constructor caso:"
c3 = New ClassCirculo(coord1, 34.3)

' Forma: clase coordenda1,coordenado2,coordenada3 (circulo que pase por 3 puntos)
Print "Constructor caso:"
c4 = New ClassCirculo(coord1, coord2, coord3)

 Hacer una Copia de un Objeto:

Para copiar variables en Gambas, usamos el operador igual "=".
Por ejemplo
-->
dim Nombre as string

dim NombreCopia as string

Nombre=”Julio”
NombreCopia=nombre

print Nombre, NombreCopia

Sin embargo, con objetos no podemos hacer lo mismo, ya que  el operador igual ("=") lo que hace es copiar la referencia del objeto (no hace copia). Cualquier cambio que haríamos en el objecto copiado modificaria al objeto original.

-->
Print "Coordenadas Mal copiadas por referencia!!!: "

Print coord1.escribe()
Print coord2.escribe()

coord2.y = 234343455

Print "Coordenadas: "
Print coord1.escribe()
Print coord2.escribe()

Obtenemos en pantalla...
Coordenadas Mal copiadas por referencia!!!:
 X: 23 Y: 34
 X: 23 Y: 34
Coordenadas:
 X: 23 Y: 234343455
 X: 23 Y: 234343455
Como ves, al alterar la clase copiada (coord2), se modifica tambien la clase origen (coord1)

Para copiar objetos, tenemos que hacerlo mediante lineas de código, que se encarguen de copiar y asignar las propieades (sus valores) del objeto Origen a las otras propiedades del objeto_copia. Por ejemplo, con este método dentro de la clase ClassCoordenada
-->
Public Function copia(origen As ClassCoordenada) As ClassCoordenada
Me.x = origen.x
Me.y = origen.y
End


'Esta es la opcion más correcta vease comentarios...


Public Sub copia(origen As ClassCoordenada)

Me.x = origen.x
Me.y = origen.y

End


Para usarlo:
-->
coord1 = New ClassCoordenada
coord2 = New ClassCoordenada

coord1.x = 23
coord1.y = 34


coord2.copia(coord1)
Print "Coordenadas: Copia mediante metodo "
Print coord1.escribe()
Print coord2.escribe()
coord2.y = 3433445

Print "Coordenadas Cambio: "
Print coord1.escribe()
Print coord2.escribe()

Y es resultado...
Coordenadas:  Copia mediante metodo
 X: 23 Y: 34
 X: 23 Y: 34
Coordenadas Cambio:
 X: 23 Y: 34
 X: 23 Y: 3433445
Como veis, ahora si, se copia bien, ya que al modificar la clase copiada, la de origen no se modifica.

Para terminar, tambien podemos usar el método contructor para que crear una copia de un objeto:
-->
Public Sub _new(Optional parametro1 As Variant, Optional parametro2 As Variant, Optional parametro3 As Variant)

Dim clase As String
If TypeOf(parametro1) = gb.Object And If Object.Type(parametro1) = "ClassCirculo" Then

hy = parametro1.y
hx = parametro1.x
hradio = parametro1.radio
estado = parametro1.leerEstado()
Return
Endif
…..
…..

Y usarlo asi:
-->
Print "Proceso de copia de circulo.."
copia = New ClassCirculo(c1)



Para más información: http://www.gambas-es.org/viewtopic.php?f=1&t=100&highlight=objetos

Descarga del Código Fuente de ejmplo. tanto de Sobre Constructores y Copia de Objetos : Enlace


Nota: Actualizacion 3/8/2013 (new)
Tras estudiar un ejemplo del foro de gambas de Francia (http://www.gambasforge.org/code-62-copier-un-controle-ou-un-conteneur-et-ses-enfants.html), sobre como clonar controels, he realizado  algunos cambios para que nos sirva para objetos complejos (con propiedades que sean arrays, clases, variant, ...)

Os dejo aqui la función que lo hace:


Public Sub CopyObject(hObjeto As Object) As Object
Dim hNewMe As Object = Object.New(Object.Type(hObjeto)) 'se genera un objeto de la mismo tipo que nos pasan
Dim s As String 'variable utilizada para almacenar el valor de hclass.symbols
Dim hclass As Class = Object.Class(hObjeto) 'contiene el valor de la clase del objeto
Dim hChild As Object
Dim nuevoObjeto As Object
Dim caso As Integer 'tipo de dato de la propiedad del objeto
Dim contador, a As Integer 'para usarlo en bucle si son arrays
Dim arrayPosible As Object
'Inicio de la copia de las propiedades....
For Each s In hclass.Symbols
If s = "Chlidren" Then Continue
If Not hclass[s].ReadOnly Then
caso = TypeOf(Object.GetProperty(hObjeto, s))
Select Case caso
Case gb.Object
'la propiedad es un objeto, vuelvo a llamar a la funcion para que me extraiga los valores y no las referencias
nuevoObjeto = CopyObject(Object.GetProperty(hObjeto, s))
Object.SetProperty(hNewMe, s, nuevoObjeto)
Try contador = Object.GetProperty(hObjeto, s).count
If Error Then
'no es un array
Else
For a = 0 To contador - 1
nuevoObjeto.add(Object.GetProperty(hObjeto, s)[a])
Next
Endif
Default
'la propiedad no es un objeto, copio el valor
Object.SetProperty(hNewMe, s, Object.GetProperty(hObjeto, s))
End Select
Endif
Next
'devuelvo la copia del objeto
Return hNewMe
End

Y un ejemplo:
http://jsbsan.blogspot.com.es/2013/08/poo-en-gambas3-copiar-objetos.html

Nota 2:
Esta función copia tanto las propiedades como los métodos que tenga el objeto.
No copia variables publicas definidas dentro del objeto (ni por supuesto las privadas)









5 comentarios:

  1. Hola,
    quisiera preguntarte algunas dudas del apartado "Para copiar objetos".

    La primera es que en la función copia no has puesto ningún return. ¿El return no es necesario en este caso? ¿Las funciones no tienen que utilizar return obligatoriamente?

    La segunda duda es que dentro de esta función copia haces referenca a ME. ¿Este ME es el objeto de la ClassCoordenada que devuelve la función?

    La tercera es que no entiendo cuando llamas a la función mediante coord2.copia(coord1), ¿qué es lo que ocurre con el objeto devuelto por la función? ¿Cómo lo interpreta gambas?

    Gracias y excúsame por la batería de preguntas, mi nivel aún es un poco bajo.

    Pablo

    ResponderEliminar
  2. 1º Pregunta: ¿El return no es necesario en este caso?
    En este caso no, ya que no devuelve ningun valor, lo que hace es asignarse a las propiedades del objeto, unos valores externos ("origen")

    El código seria "correcto" asi:
    Public Sub copia(origen As ClassCoordenada)

    Me.x = origen.x
    Me.y = origen.y

    End

    El tema es que, tanto si usas sub como function, hace lo que me interesa y no muestra ningun error... Lo correcto es usar "sub"

    2) ¿Las funciones no tienen que utilizar return obligatoriamente?
    Si. si es para devolver algo... en este caso no devulve nada...
    Gambas en este caso es ambigüo, puedes hacer estas cosas y no daria error (ni te lo muestra). Lo suyo es haber definido como subrutina (sub) y no como funcion (function).

    3) "ME", hace referencia al objeto actual.
    Fijate como se usa el método copia:
    coord2.copia(coord1)

    Los valores de las propiedades del objeto coord1, son copiadas al objeto coord2, en el codigo fuente se pone "Me" para hacer referencia al mismo objeto del que se esta usando el método "copia"

    4) La tercera es que no entiendo cuando llamas a la función mediante coord2.copia(coord1), ¿qué es lo que ocurre con el objeto devuelto por la función? ¿Cómo lo interpreta gambas?

    Como te comento mas arriba, la funcion no devuelve nada.
    Lo que Gambas hace es los valores de coord1 (propiedades "x" e "y"), los asigna a las propiedades "x" e "y" del objeto coord2.

    Nota:
    Cuando se habla de objetos, normalmente no se habla de funciones ni de subrutinas, sino de métodos.
    Los métodos pueden o no devolver valores, y son los encargados de trabajar con las variables internas del objeto.

    Nota:
    Otra cosa que vi es que un "sub" tambien puede devolver valores, si pones un return. Ya te digo gambas es ambiguo...

    "Gracias y excúsame por la batería de preguntas, mi nivel aún es un poco bajo."
    Gracias a ti por preguntar, si tienes alguna duda me lo comentas.

    ResponderEliminar
  3. Hola,

    muchas gracias por todas tus aclaraciones, ahora lo he entendido todo mucho mejor. Me encontraba con el problema de copiar objetos, y este post me ha sido de mucha utilidad. Bueno, a lo mejor en un futuro gambas nos permite copiar "automáticamente" objetos, con la misma facilidad que cuando copiamos variables, pero de momento hay que hacerlo "a mano".

    Gracias de nuevo.

    Un saludo.

    Pablo

    ResponderEliminar
  4. Me alegra que te sea de utilidad.


    Nota:
    Modifico el articulo, para incluir la solución "correcta" y asi quede todo más claro

    ResponderEliminar
    Respuestas
    1. He añadido una nueva nota "Actualizacion 3/8/2013" al final del articulo, donde presento la forma de copiar objetos complejos.

      Eliminar

A falta de donativos... un poco de publicidad