Ir al contenido



Foto

Programando a través de POO - la clase TPartidos (Haciendo las cosas bien)


  • Por favor identifícate para responder
25 respuestas en este tema

#1 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 10:48

Amigos, abro éste tema que a la vez estoy seguro que la final será de mucha ayuda para otros, como les había comentado en un hilo anterior que iba a tener que cambiar las cosas, como sabrán, yo aprendí a programar de manera procedural y a través de funciones (Dicho vulgarmente de manera desorganizada) y quiero cambiar eso, tengo un proyecto que apenas he empezado y decidí que antes de continuar mejor haría las cosas bien, pues bien, por favor, no me remitan a la cara oculta de delphi 4, he dado unas releídas y la verdad siento que hay huecos y no logro captar algunas cosas, solamente voy a tratar con la clase TPartidos, (La aplicación se basa en un sistema de puntos del juego de billar) y en base a lo aprendido con ésta clase iré aplicandola en las demás clases, empecemos, pues, la clase debería de registrar la información un partido de billar dado, además de obtener la información del mismo y listar una cantidad x de partidos registrados, he declarado la clase de la siguiente forma:


delphi
  1. type
  2. TPartido = class
  3. FGanador: Integer; //Id del ganador
  4. FPerdedor: Integer; //Id del Perdedor
  5. FWinnerAlias: String; //El alias del ganador
  6. FLoserAlias: String; //El alias del Perdedor
  7. FDistancia: integer; // La cantidad de juegos que debe ganar un jugador
  8. FWinsGanador: integer; //Los partidos ganados por el ganador
  9. FWinsPerdedor: integer; //Los partidos ganados por el perdedor
  10. end;

¿Es correcto la declaración?, ¿debo agregar algo más en la declaración class?, ¿debe ser un objeto?, cabe declarar que eso es sólo una parte, porque las informaciones a registrar es un poco extensa.

 

Mientras seguiré, ahora, si quiero registrar una partida (Normalmente es cómo lo he estado haciendo en otros proyetos) declaro la función ó procedimiento:


delphi
  1. type
  2. TPartido = class
  3. ... //FGanador, etc
  4. ...
  5. function CrearPartido: Boolean;
  6. end;

¿Es correcto esa declaración?, ¿En qué debo declarar ese tipo de función ó procedimiento?,¿Public, private, published?, la función ó procedimiento deberá guardar la información a través de REST api (el servidor está hecho en Slim Framework PHP).

 

Por ahora esa es la primera parte, luego de sus respuestas seguiré ampliando mis dudas.

 

Saludos.


  • 0

#2 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 17 marzo 2017 - 11:59

Hola enecumene

 

Como yo veo las cosas, en el primer caso, lo que tu denominaste TPartido esta medio fuera de lo que es POO. Osea, fijate que no tiene ningun comportamiento (que hace la clase? que metodos podria tener?). Es basicamente una "bolsa de getter/setters", osea solo contiene datos. Recuerda siempre que POO = datos + comportamiento. Lo que tenes en frente se conoce como DTO, o domain transfer object. Es basicamente una pequeña estructura de datos que agrupa informacion que luego tiene que enviarse a otro lado. Existen solamente porque es mucho mas comodo de trabajar y no tener que llamar a metodos y pasar un monton de parametros. Por lo general son entradas y salidas hacia "afuera" de la aplicacion (o estan muy cerca de estos lugares), justamente porque para comunicar dos maquinas, lo unico que podes pasar es un mensaje con texto plano (o algo de nivel mas bajo en "binario puro" o raw) el cual luego es convertido en este intermediario que es mas facil de tratar. Para lo que te sirve tambien es para abstraerte del protocolo de transferencia entre las distintas aplicaciones. Hoy estas comunicandote con una API Rest, supongamos que con xml, pero bien podria ser json mañana, este DTO te deberia servir para cualquiera de los dos casos, luego ya habria un verdadero objeto que brinde el srevicio de las conversiones para adentro y para afuera

 

 

En muchos lenguajes estos tambien son conocidos como los POXO, es decir Plain Old "X" Object, donde la X cambia segun el nombre del lenguaje, (como la familia XUnit para unit testing), por ej: POJO = Plain Old Java Object, etc

 

En Delphi tenemos la suerte de que no hace falta declarar una clase o una interfaz para este tipo de cosas. Se puede usar un record de toda la vida, que son mucho mas livianos y ademas hay algunos framework que pueden facilitarle la tarea de las conversiones usando Rtti (tendrias que seguir algunas reglas, por ejemplo, los nombres, tipos y visibilidades de los campos, el uso de algun atributo, etc). Este articulo puede interesarte

 

Con respecto a lo de TPartido, en realidad lo que se espera de las funciones es que sean "respuestas" a "preguntas". Si vos tenes una funcion llamada "CrearPartido", estas realizando una mutacion al estado del programa. Estos cambios de estado, comandos, u ordenes son mejor representarlas con procedimientos (procedures en Pascal). Piensa en los constructores: no te devuelven True si se construyo correctamente un objeto, en el peor de los casos obtendrias una excepcion. Para poder invocar a "CrearPartido" es necesario que exista una instancia de esa clase previamente creada sobre la cual invocas ese metodo. Crear una clase y luego tener que preguntar si esta "apta" para trabajar no tiene sentido alguno y solamente complica las cosas

 

Si uno crea una clase lo que se espera es que inmediatamente este lista para trabajar, y no que haya que asegurarse de eso. Eso no quiere decir que antes haya que poner o cambiar algunas propiedades para obtener el resultado esperado, pero preguntar "se creo correctamente el partido? ok entonces incrementar contador del jugador A en 1, sino.."

 

 

A mi me parece que si hoy estas pensando que tu clase TPartidos tiene que enviar la info de los partidos a la API Rest, creo que podria decirse que en realidad lo que esta haciendo es encargarse de la persistencia de la aplicacion. Osea vendria a ser el repositorio de partidos. Este es un buen candidato para poner detras de una interfaz que defina como se envia esta informacion, y de ser necesario, como se recupera. Por ejemplo podrias tener una interfaz asi:


delphi
  1. type
  2. TPartido = record
  3. end;
  4.  
  5. IRepositorioPartidos = interface
  6. procedure Guardar(const Partido: TPartido);
  7. procedure Eliminar(const Partido: TPartido);
  8. function Seleccionar: TList<TPartido>;
  9. end;

Esto te permite abstraerte de la API Rest, al menos momentaneamente, y podrias ir usando para las pruebas alguna clase que implemente esos metodos pero usando una variable de tipo lista que actue de "base de datos" o de "webservice". Luego es cuestion de reemplazar la implementacion por la verdadera

 

En ese ejemplo puse un metodo eliminar. No se si es necesario, simplemente lo puse para decirte que si hay algo que no necesitas, no malgastes el tiempo siquiera en definirlo. Si en tu aplicacion el metodo Eliminar no es necesario, ni lo pongas en la interfaz. Codifica solo lo minimo indispensable


  • 0

#3 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 17 marzo 2017 - 12:13

Otra cosa, me alegro que decidas cambiar la forma de programar. Pero ya te adelanto, como se lo digo a todos, que a la primera no va a salir bien, ni a la segunda, ni a la tercera. Con "bien" me refiero a que por mas empeño que haya, incluso entre  todos, defectos seguro que apareceran

 

Y ahora por mas insolito que te parezca, creo que un proyecto "viejo", la "porqueria", lo legado, es mejor partir de eso y refactorizarlo en algo mejor estructurado que algo "nuevo". El motivo es porque el proyecto anterior lo conoces, no solo el codigo y su estructura y como funciona, sino tambien el dominio, y tambien sabes donde estan los problemas, si te pasa que tenes que mantener un proyecto de este estilo, seguramente sabes que "vaya. este modulo esta todo mal, siempre que toco aqui, se me rompe esta funcion, o tengo que modificar el otro modulo".

 

Quiza a primera vista suene mas tentador "emepzar de cero" con algo nuevo y "hacerlo bien". Pero la realidad es que por mas que quieras hacerlo bien, anticiparte a los problemas puede ser peor que programar "mal", "procedimental", o simplemente "escribir la porqueria de codigo". La razon es la estadistica. Existe algo se llama "regla de tres", que mas o menos enuncia lo siguiente:

 

1. Escribes un codigo "X"

2. En algun momento, vuelves a escribir el codigo "X", o bien, escribes un codigo muy similar a "X", por ej: "Xa" o "aX"

3. Te das cuenta: "mierda! tengo codigo duplicado, vamos a solucionarlo". En este punto debes resistir con toda tu fuerza a la tentacion de hacer algo; es decir, no haces nada

4. Por tercera vez, ocurre el punto 2. Es recien este momento en cuando dices: ok, veamos como solucionar esto

 

Y vuelvo a decir que la razon es estadistica, como en matematicas. Si vos tenes apenas un codigo repetido dos veces, no es el fin del mundo. Si prematuramente lo refactorizas, seguramente tomes una decision poco acertada simplemente por que te falta informacion. Dos veces es muy poco. No sabes porque exactamente se repite el codigo, o si es muy parecido, que "tantas variaciones puede haber", o como ocurren esas variaciones. Lo mejor es esperar un poco y ver como evoluciona

 

Ahora, por supesto, el numero "tres" es un valor totalmente arbitrario. No es una regla de oro que hay que seguir a rajatabla. En tu caso el numero podria ser 17 si te parece bien ese valor. La idea es tomar las decisiones cuando se tiene informacion, y de esta manera, tomar la mejor decision, y no apurarse

 

En este articulo esta explicado de manera brillante:

 

https://lostechies.c...-rule-of-three/

 

Y aqui tambien pero de manera mas tecnica

 

http://blog.ploeh.dk.../08/07/why-dry/


  • 0

#4 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 12:53

Muchas gracias Agustín, sobre que me quede bien hasta la quinta vez al menos, estoy consciente de eso, de todos modos no es un proyecto prioritario, así que me tomaré mi tiempo, bien, entonces me recomiendas usar el record para paso de información y el uso de interface, la verdad no estoy empapado sobre éste último, pero si es la mejor manera, pues allá vamos, viendo tu ejemplo anterior, TPartido sería de la siguiente forma:


delphi
  1. type
  2. TPartido = record
  3. Ganador: integer;
  4. Perdedor: Integer;
  5. WinnerAlias: String;
  6. LoserAlias: String;
  7. //...Más
  8. end;

¿Es correcto?, viendo el ejemplo de interface, ¿cómo se instanciaría esa interface ó se crea?, asumo que es de la siguiente manera:


delphi
  1. var unPartido: TPartido;
  2. unRepo: IRepositorioPartidos;
  3. begin
  4. unPartido.Ganador := 'Fernando';
  5. unPartido.Perdedor := 'Efren';
  6. //...Más
  7.  
  8. //¿Se crea?
  9. unRepo := IRepositorioPartidos.Create;
  10.  
  11. //ó Simplemente llamo al método
  12. unRepo.Guardar(unPartido);
  13.  
  14. //¿?
  15. unRepo.Free;
  16. end;


  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 13.555 mensajes
  • LocationMéxico

Escrito 17 marzo 2017 - 12:57

Suscribo todo lo dicho por Agustín, excelente participación y sin desperdicio.

 

Saludos


  • 0

#6 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 17 marzo 2017 - 01:31

 

Muchas gracias Agustín, sobre que me quede bien hasta la quinta vez al menos, estoy consciente de eso, de todos modos no es un proyecto prioritario, así que me tomaré mi tiempo, bien, entonces me recomiendas usar el record para paso de información y el uso de interface, la verdad no estoy empapado sobre éste último, pero si es la mejor manera, pues allá vamos, viendo tu ejemplo anterior, TPartido sería de la siguiente forma:


delphi
  1. type
  2. TPartido = record
  3. Ganador: integer;
  4. Perdedor: Integer;
  5. WinnerAlias: String;
  6. LoserAlias: String;
  7. //...Más
  8. end;

¿Es correcto?, viendo el ejemplo de interface, ¿cómo se instanciaría esa interface ó se crea?, asumo que es de la siguiente manera:


delphi
  1. var unPartido: TPartido;
  2. unRepo: IRepositorioPartidos;
  3. begin
  4. unPartido.Ganador := 'Fernando';
  5. unPartido.Perdedor := 'Efren';
  6. //...Más
  7.  
  8. //¿Se crea?
  9. unRepo := IRepositorioPartidos.Create;
  10.  
  11. //ó Simplemente llamo al método
  12. unRepo.Guardar(unPartido);
  13.  
  14. //¿?
  15. unRepo.Free;
  16. end;

 

 

Nop, como charlamos en este hilo (el cual te recomiendo que leas lo que pusimos Delphius y yo), las interfaces deben implementarse.

 

Una interfaz es un "tipo de datos", el cual solamente actua de protocolo. Osea, es ponerse de acuerdo en como se llaman los metodos, que parametros tienen, y de que tipo son. Por eso es que todo lo que define la interfaz es publico. Ademas las interfaces no tienen permitido definir variables de instancia, es decir, no tienen, a priori, estado, definen comportamiento puro. Esto no te exime de tener metodos "getters o setters", y en Delphi hasta podes tener propiedades. Otra cosa importante es que no se permite implementar ningun metodo, osea seria como que todos los metodos sean publicos, abstractos y virtuales; aun asi, una clase que implementa una interfaz tranquilamente puede poner los metodos como privados. Esto suelo hacerlo mucho ya que es una manera de "forzar" a utilizar la interfaz y no la implementacion

 

Tambien seria totalmente valido usar clases abstractas. Hay algunas diferencias, sintacticas, semanticas, y algunas cuestiones de cada lenguaje (pero ya es hilar muy fino). En sintesis las interfaces son "mas abstractas" que las clases abstractas, porque una clase abstracta se puede permitir tener variables de instancia y ademas implementar metodos si quisiera

 

Para poder usar una interfaz primero tenes que crear una implementacion. Implementar una interfaz significa crear una clase que implementa todos los metodos detallados en la interfaz. Esto es obligatorio. El compilador no te va a dejar tener una clase que no implemente todos los metodos de una interfaz

 

Asi declaras una interfaz


delphi
  1. type
  2. IMiInterfaz = interface
  3. // en la vasta mayoria de los casos es muy util generar un guid
  4. // esto lo haces poniendo el cursor justo debajo de la linea que le da el nombre
  5. // a la interfaz y presionando Control+Shift+G. Te recomiendo que mires la ayuda de Delphi
  6. // para aclararse para que es necesario este numero
  7. procedure Ejecutar;
  8. end;

Y asi declaras que implementas una interfaz


delphi
  1. type
  2. // una clase puede implementar mas de una interfaz si quisiera
  3. TMiInterfaz = class(clase ancestro, interfazImplementada1, ..., interfazImplementadaN)
  4. // se deben implentar todos los metodos de las interfaces que decimos que implementamos
  5. end;

En Delphi, del mismo modo que todas las clases heredan, quierase o no, de TObject, las interfaces derivan todas de IInterface. Esta interfaz define 3 metodos, por lo tanto, siempre va a ser obligatorio implementar estos 3 metodos. Delphi provee algunas clases que implementan IInterface para ahorrarnos ese trabajo

 

Hay varias, pero por el momento, hay dos, quiza tres, que vale la pena mencionar

 

TInterfacedObject: esta clase esta definida en la undiad System y es una subclase de TObject que solamente implementa IInterface y nada mas. Basicamente realiza el conteo de referencias, es decir, la memoria se maneja "automaticamente" como los string y los record. El objeto se destruye solo cuando ninguna variable apunta a el. Con esto te ahorras los llamados a .Free

 

TComponent: que esta definido en System.Classes. Esta clase lo que hace es "desactivar" el conteo de referencias, e implementar otro esquema de memoria en la cual el "dueño" (owner) del componente se encarga de liberarlo. Por ejemplo todos los componentes y controles que estan en un Form, se destruyen cuando se destruye el Form. Si un componente no tiene su dueño, el responsable de liberarlo de memoria es el programador

 

TSingletonImplementation: definida en System.Generics.Defaults, esta tambien desactiva el conteo de referencias y es util para implementar justamente Singletons

 

Aca esta la ayuda de Delphi para el uso de interfaces: http://docwiki.embar...sing_Interfaces

 

El conteo de referencias, explicado bastante rapido y de manera practica, es mas o menos de esta manera. Cuando tenes una variable o campo, o mejor dicho una referencia a una interfaz, el compilador implicitamente hace algo de este estilo:


delphi
  1. procedure AlgunProceso;
  2. var
  3. Interfaz: IInterface;
  4. begin
  5. // en este punto Interfaz = nil, osea, no hay una referencia a ninguna interfaz
  6. Interfaz := TInterfacedObject.Create;
  7. // al asignar el objeto, Interfaz es una referencia a una interfaz IInterface,
  8. // y su contador de referencias es 1
  9. end

En el momento que esa variable Interfaz, se va del "scope", esto quiere decir, cuando termina el procedimiento, o si la interfaz es una variable de instancia de la clase X, cuando la clase X es destruida, el conteo de referencias decrementa. Cuando llega a 0, el objeto se destruye solo

 

Ahora, si yo tuviera este codigo:


delphi
  1. // programa principal
  2. var
  3. Intf: IInterface;
  4. begin
  5. Intf := TInterfacedObject.Create; // conteo de ref. 1
  6. UsarInterfaz(Intf);
  7. // termina el programa, la variable Intf "se va del alcance (scope)", por lo tanto
  8. // se decrementa su conteo de ref. y pasa a 0, entonces se destruye
  9. end;
  10.  
  11.  
  12. procedure UsarInterfaz(Intf: IInterface);
  13. begin
  14. // cuando este procedimiento es invocado, el compilador pasa el parametro y en ese momento
  15. // el conteo de referencias es 2
  16.  
  17. // trabajar con la interfaz
  18.  
  19. // fin del procedimiento. Cuando termina, se decrementa en 1 el conteo de referencias, es decir,
  20. // el conteo de referencias es 1 y la interfaz "sigue viva"
  21. end; 

En resumen:

 

1. Defines la interfaz

2. Creas una clase que la implementa

3. Para usarla, instancias esa clase, pero cuando declaras el tipo de la variable, el chiste a todo esto es usar el tipo de la interfaz; no de la clase

 

 

Tu ejemplo seria:


delphi
  1. var unPartido: TPartido;
  2. unRepo: IRepositorioPartidos;
  3. begin
  4. unPartido.Ganador := 'Fernando';
  5. unPartido.Perdedor := 'Efren';
  6. //...Más
  7.  
  8. // cambie la I por una T, pero podria ser cualquier clase, siempre y cuando implemente
  9. // IRepositorioPartidos. De no ser asi, se produce error de compilacion
  10. unRepo := TRepositorioPartidos.Create;
  11.  
  12. unRepo.Guardar(unPartido);
  13.  
  14. end;


  • 0

#7 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 01:42

Más que claro Agustín, voy a ir aplicandolo a mi proyecto, e iré comentando los progresos.

 

Saludos.


  • 1

#8 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 04:34

Retomando el tema, llevo lo siguiente:


delphi
  1. TPartidoRec = record
  2. Id: Integer;
  3. Ganador: Integer;
  4. Perdedor: Integer;
  5. WinnerAlias: String;
  6. LoserAlias: String;
  7. WinsGanador: Integer;
  8. WinsPerdedor: Integer;
  9. Distancia: Integer;
  10. PuntosGanador: Integer;
  11. PuntosPerdedor: Integer;
  12. NewPuntosGanador: Integer;
  13. NewPuntosPerdedor: Integer;
  14. WinPercentGanador: Extended;
  15. WinPercentPerdedor: Extended;
  16. end;
  17.  
  18. IPartidos = interface
  19. function ListarUltimosPartidos(const ARows: String): TList<TPartidoRec>;
  20. end;

Al hacer la implementación de la función no me aparece, pero hice lo siguiente:


delphi
  1. TPartido = class(TInterfacedObject,IPartidos)
  2. function ListarUltimosPartidos(const ARows: String): TList<TPartidoRec>;
  3. end;

Aquí sí me aparece la función, ¿Voy bien?.


  • 0

#9 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 17 marzo 2017 - 05:07

No he entendido esto: 

 

Al hacer la implementación de la función no me aparece, pero hice lo siguiente:

 

 

:|


  • 0

#10 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 05:18

No he entendido esto: 

 

 

:|


delphi
  1. implementation
  2.  
  3. function TPartido.ListarUltimosPartidos(const ARows: string): TList<TPartidoRec>;
  4. begin
  5. //Codigo
  6. end;


  • 0

#11 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 17 marzo 2017 - 05:33

Ah, claro porque recuerda, la interfaz no implementa metodos. Simplemente esta para decir cual es la funcionalidad disponible


  • 0

#12 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 05:55

Bien, eso quiere decir que voy bien :) , continuando con la función ListarUltimosPartidos(), el código es el siguiente, recordar que recibo los datos desde un servidor REST:


delphi
  1. function TPartido.ListarUltimosPartidos(const ARows: string): TList<TPartidoRec>;
  2. var aJson: String;
  3. aTask: ITask;
  4. AList: TList<TPartidoRec>;
  5. begin
  6. AList := TList<TPartidoRec>.Create;
  7.  
  8. aTask := TTask.Create(
  9. procedure
  10. var
  11. AArray: TJSONArray;
  12. AValores: TJSONValue;
  13. Partido: TPartidoRec;
  14. begin
  15. aJson := Rutinas.GetRequest('/partidas/filtro/' + ARows);
  16.  
  17. AArray := TJSONObject.ParseJSONValue(aJson) as TJSONArray;
  18.  
  19. for AValores in AArray do begin
  20. Partido.Id := AValores.GetValue<integer>('id');
  21. Partido.WinnerAlias := AValores.GetValue<string>('winner');
  22. Partido.LoserAlias := AValores.GetValue<string>('loser');
  23. Partido.WinsGanador := AValores.GetValue<integer>('wins1');
  24. Partido.WinsPerdedor := AValores.GetValue<integer>('wins2');
  25. Partido.ModalBall := AValores.GetValue<string>('bola2');
  26.  
  27. //Agregamos a la lista
  28. AList.Add(Partido);
  29. end;
  30. end);
  31.  
  32. aTask.Start;
  33.  
  34. result := AList;
  35. end;

Ahí cargo las últimas partidas pasandole la cantidad de filas a devolver, en el siguiente código hago la llamada:


delphi
  1. var
  2. LPartidos: TList<TPartidoRec>;
  3.  
  4. ...
  5.  
  6. procedure TForm1.button1Click(Sender: TObject);
  7. var Partidos: IPartidos;
  8. begin
  9. Partidos := TPartido.Create;
  10. LPartido := Partidos.ListarUltimosPartidos('25');
  11.  
  12. ShowMessage(IntToStr(Partidos.Count)); //Me Devuelve 25
  13. end;

¿Vamos bien?.


  • 1

#13 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 06:47

Hasta ahora todo va bien, puedo iterar la lista y mostrarlo en el ListBox siempre y cuando sea desde un Botón, si lo hago en el FormShow del formulario donde cargo los datos en la lista LPartidos (TList<TPartidoRec>) no me carga el ListBox, no sé cómo se maneja esa parte, pero desde un botón todo bien, o sea, si hago esto:


delphi
  1. procedure TForm1.ListarPartidos;
  2. var i: Integer;
  3. AItem: TListBoxItem;
  4. begin
  5. Lista.beginUpdates;
  6. for i := 0 to LPartidos.count -1 do begin
  7. //Cargamos los partidos en la lista
  8. end;
  9. end;
  10.  
  11. procedure TForm1.FormShow(Sender: TObject);
  12. var Partidos: IPartidos;
  13. begin
  14. Partidos := TPartido.create;
  15. LPartidos := Partidos.ListarUltimosPartidos('25');
  16.  
  17. ListarPartidos;
  18. end;

No carga el Listbox, si hago esto:


delphi
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3. ListarPartidos;
  4. end:

Sí me carga el listBox, es un detalle menor, por otra parte, cómo vuela!.

 

Me está agradando el asunto, espero no tener que toparme con desagradables sorpresas :D


  • 0

#14 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 17 marzo 2017 - 07:52

A mi me parece que vas bien
 
Con respecto al ListBox me parece que te falta el EndUpdate, puesto que pusiste un BeginUpdate
 
Otra cosa que podes hacer y que puede ser interesante si queres ganar mas velocidad, es en lugar de ir llenando cada registro leyendo el json, darle todo el json entero al registro y luego dentro del record hacer cosas como

delphi
  1. type
  2. TPlayerRec = record
  3. private
  4. FJSON: TJSONValue;
  5. FWinnerAlias: string;
  6. function GetWinnerAlias: string;
  7. public
  8. constructor Create(const JSON: TJSONValue);
  9. property WinnerAlias: string read GetWinnerAlias
  10. end;
  11.  
  12. implementation
  13.  
  14. constructor TPlayerRec.Create(const JSON: TJSONValue);
  15. begin
  16. FJSON := JSON;
  17. end;
  18.  
  19. function TPlayerRec.GetWinnerAlias: string;
  20. begin
  21. if FWinnerAlias = EmptyStr then
  22. FWinnerAlias := FJSON.GetValue<string>('winner');
  23.  
  24. Result := FWinnerAlias;
  25. end;

Osea, no realizar las lecturas de cada campo JSON hasta que sea estrictamente necesario, a menos que sea muy pero muy seguro que vas a leer siempre todo. Es solo una pequeña optimizacion  (y). Esto se llama "inicializacion perezosa" o simplemente lazy
 
Por cierto, otra cosa que podes hacer es activar el ReportMemoryLeaksOnShutdown, simplemente le asignas True en el crear del form principal o en las primerias lineas del proyecto cuando se inicializa la aplicacion. Es para ir detectando problemas de fuga de memoria que se te puedan escapar por ahi
  • 0

#15 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 17 marzo 2017 - 08:16

¡Genial!, ya luego sigo comentando mis avances.


  • 0

#16 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 20 marzo 2017 - 07:14

Quizás esté saliendome un poquito del tema, pero está relacionado, entre la función ListarUltimosPartidos(): TList<TPartidoRec> y ListFilteredeGames Tuve que colocar un Sleep de 2 segundos, ya sabeis que el primero llena un TList con los últimos partidos y el segundo extrae la información en el TList a un listbox, debido a la razón que desconozco el segundo no logra obtener los datos, pero si le doy un tiempo sí obtiene los datos, o sea:


delphi
  1. procedure TForm1.FormShow(Sender: TObject);
  2. begin
  3. Partidos := TPartido.create;
  4. LPartidos := Partidos.ListarUltimosPartidos('25');
  5.  
  6. listFilteredGames; //No Obtiene los datos!!! (0)
  7. end;
  8.  
  9. procedure TForm1.FormShow(Sender: TObject);
  10. begin
  11. Partidos := TPartido.create;
  12. LPartidos := Partidos.ListarUltimosPartidos('25');
  13.  
  14. Sleep(2000);
  15.  
  16. listFilteredGames; //Sí Obtiene los datos!!! (25)
  17. end;

¿A qué se deberá eso?, por primera estoy trabajando con TList y tal vez algo se me escapa.


  • 0

#17 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 20 marzo 2017 - 08:22

No estoy muy seguro de esto ultimo, pero creo que esta relacionado con el codigo relacionado a la tarea (lo del ITask), el tema es asi, tu metodo ListarUltimosPartidos es asincronico, es decir, lo llamas e inmediatamente retorna y pasa a la siguiente instruccion. Si la lista no estaba terminada para el momento en que se ejecuta la instruccion que muestra los items en el Combo, bueno, no lista nada  :(

 

Pero antes de estar seguro yo te diria que saques momentaneamente el tema de la tarea para ver si es eso


  • 0

#18 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 20 marzo 2017 - 01:41

EL método ListarUltimosPartidos no interviene ITask, sí en ListFilteredGames, momentáneamente lo quité, y el efecto es el mismo, sólo lista luego de un Sleep(), por el momento sólo es este caso dado que no he avanzado más de ese punto, haciendo prueba y error, cuando se maneja TList(), ¿hay algo más que considerar en esos casos?.


  • 0

#19 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 770 mensajes
  • LocationArgentina

Escrito 20 marzo 2017 - 03:01

Ah en el código de arriba si usas task. Bueno es raro lo que comentas, no podes publicar esos dos códigos y el como los invocas? Otro problema que podes tener es manejo de punteros... Pero me extraña lo del sleep. Tiene más pinta de ser problema de interferencia de hilos
  • 0

#20 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.183 mensajes
  • LocationRepública Dominicana

Escrito 20 marzo 2017 - 06:32

Disculpa Agustín, es cierto que en ambos métodos sí intervenía el ITask, lo que hice fue quitarlo en ambos casos y unirlo en un sólo procedimiento llamando en el onShow del formulario:


delphi
  1. procedure TFMain.LoadGames;
  2. var aTask: ITask;
  3. begin
  4. aTask := TTask.Create(
  5. procedure
  6. begin
  7. Partidos := TPartido.Create;
  8. LPartidos := Partidos.ListarUltimosPartidos('25');
  9.  
  10. TThread.Queue(nil,
  11. procedure
  12. begin
  13. listFilteredGames;
  14. end);
  15. end);
  16.  
  17. aTask.Start;
  18. end;

Ahora sí trabaja correctamente, ya retomaré el tema de nuevo.

 

Saludos.


  • 1