Ir al contenido



Foto

Programacion "fluida" o fluent


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

#1 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 30 agosto 2016 - 09:14

Como pequeña anecdota introductoria, hoy estaba escribiendo el tipico codigo que conecta un TAction con su(s) respectivos ShortCuts o atajos de teclado

 

De pronto me encuentro con que en la gran mayoria de los casos tengo que escribir algo como esto


delphi
  1. Action_VerDatos.ShortCut := ShortCut(VK_F1, []);
  2. Action_ModificarDatos.ShortCut := ShortCut(VK_F2, []);
  3. ..
  4. Action_BlaBla.ShortCut := ShortCut(VK_F8, []);

Y luego si no me alcanza con el ShortCut principal, se dispone de una lista de atajos secundarios, es decir una SecondaryShortCutList, la cual es un (en mi opinion) innecesario descendiente de TStringList, y se debe escribir codigo asi:


delphi
  1. Action_VerDatos.SecondaryShortCuts.Add(ShortCutToText(ShortCut(VK_F5, [])));

Ok, suficiente con una sola linea para ver que eso ya es horriblemente largo. Como siempre tenemos varios TAction con los que usamos este codigo, ver mas de 5 lineas seguidas asi a mi ya me pone de muy malas :)

 

{** Para los que dicen que se pueden configurar en el IDE... no me arruinen el tema :). Bromas aparte, hay algunos "codigos" o ShortCut que o bien no aparecen, o en el editor de strings de los secundarios no son tan triviales de escribir, de esta forma en runtime es mas seguro ** }

 

Entonces como a cualquiera se me ocurrio escribir algun procedimiento o funcion que me permita evitarme la parte repetitiva e innecesaria. Por ejemplo, si no quiero indicar el TShiftState, no hace falta tener que enviar siempre como argumento []. Y ni que hablar del .Add(ShortCutToText..)

 

Pero luego se me ocurrio que tambien podria ser divertido poder escribir el codigo usando programacion fluida, o como se le dice en ingles, las "fluent API"

 

Es decir, lo anterior se podria poner asi:
 


delphi
  1. ShortCutLinker
  2. .ClearAllShortCuts(ActionList1)
  3. .SetShortCut(Action_VerDatos, VK_F1)
  4. .SetShortCut(Action_ModificarDatos, VK_F2)
  5. .SetShortCut(Action_BlaBla, VK_F8)
  6. .ClearSecondaryShortCuts(Action_BlaBla)
  7. .AddSecondaryShortCut(Action_Blabla, VK_F4, [ssAlt]); // muy divertida esta

Para implementar algo como eso en Delphi el unico "secreto" es, todos los metodos deben ser funciones y devolver el objeto que implementa el comportamiento, es decir, siempre devolvemos Self

 

En mi caso decidi implementar usando una interface, que de momento se ve asi:
 


delphi
  1. /// <summary> Fluent interface that allows to link an Action with a ShortCut </summary>
  2. IActionShortCutLinker = interface
  3. ['{2F7A38DC-CC4A-4AE9-880C-C7891759F0DD}']
  4. /// <summary> Clears the ShortCut property of the Action </summary>
  5. function ClearShortCut(Target: TContainedAction): IActionShortCutLinker;
  6. /// <summary> Clears the Secondary ShortCuts of the Action </summary>
  7. function ClearSecondaryShortCuts(Target: TContainedAction): IActionShortCutLinker;
  8. /// <summary> Sets the Target ShortCut property </summary>
  9. function SetShortCut(Target: TContainedAction; const ShortCut: TShortCut): IActionShortCutLinker; overload;
  10. /// <summary> Creates the TShorCut for the recieved arguments and sets it to the Target ShortCut property </summary>
  11. function SetShortCut(Target: TContainedAction; const Key: Word; const Shift: TShiftState): IActionShortCutLinker; overload;
  12. /// <summary> Adds the ShortCut to the list of Secondary ShortCuts of the Action </summary>
  13. function AddSecondaryShortCut(Target: TContainedAction; const ShortCut: TShortCut): IActionShortCutLinker; overload;
  14. /// <summary> Creates the ShortCut for the received arguments and adds it to the list of Secondary ShortCuts of the Action </summary>
  15. function AddSecondaryShortCut(Target: TContainedAction; const Key: Word; const Shift: TShiftState): IActionShortCutLinker; overload;
  16. end;

Que les parece esta forma de programar? Alguna vez la han usado? Ultimamente estoy viendo que esta algo de moda.

 

De hecho en lenguajes como SmallTalk el "por defecto" es que si un metodo no devuelve nada con una sentencia return (traducido a Delphi seria nuestro Result de una function) lo que se devuelve implicitamente es el Self, y de esa forma se puede escribir codigo fluent sin necesidad de programar nada extra


  • 2

#2 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.269 mensajes
  • LocationArgentina

Escrito 30 agosto 2016 - 09:57

Estoy medio dormido a estas horas Agustín... creo entender tu concepto. Lo que me trae duda es el ejemplo de código delphi...

¿Cómo sería un ejemplo funcional de tu ShortCut fluent API?

 

Esto te lo pregunto porque al ver el código que pones primero:


delphi
  1. ShortCutLinker
  2. .ClearAllShortCuts(ActionList1)
  3. .SetShortCut(Action_VerDatos, VK_F1)
  4. .SetShortCut(Action_ModificarDatos, VK_F2)
  5. .SetShortCut(Action_BlaBla, VK_F8)
  6. .ClearSecondaryShortCuts(Action_BlaBla)
  7. .AddSecondaryShortCut(Action_Blabla, VK_F4, [ssAlt]); // muy divertida esta

Mi ojo compilador arroja error...

¿es una especie de pseudo-código? ¿Es un swith ShortCutlinker  do?

 

Saludos,

PD: Ya que lo pedías, eliminé el hilo duplicado. Gracias por avisar.


  • 0

#3 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.012 mensajes
  • LocationMéxico

Escrito 30 agosto 2016 - 10:13

Hola, muy interesante el tema y tengo una pregunta con la siguiente línea

 

.AddSecondaryShortCut(Action_Blabla, VK_F4, [ssAlt]); // muy divertida esta

 

Estás modificando el comportamiento del Alt-F4 ?

 

Saludos


  • 0

#4 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 30 agosto 2016 - 10:20

No me entendiste porque no puse la implementación completa y puede prestar a confusión
 


delphi
  1. function ShortCutLinker: IActionShortCutLinker

De ahí sale la función. Es una función global que crea un objeto que implementa la interface. La razón por la cual decidí usar interfaces y no clases es porque me ahorro el .Free

Lo que creo que te mareo es el como lo escribí. Y además ahora que lo veo, no está claro el contexto de donde se usa, parecen líneas de código locas. Todo eso iría dentro de un método

Fíjate que el chiste es que todos los métodos devuelvan Self. Osea que cuando ejecuta el primer método, el retorno sigue siendo el mismo objeto al que le mandé el primer mensaje. Entonces le mando otro, y otro, y otro

Por una cuestión estética, le di ese "formato", que es el que habitualmente veo cuando usan fluent.

Pero eso es lo mismo que esto
 


delphi
  1. procederé TForm1.FormCreate(Sender: TObject);
  2. begin
  3. ShortCutLinker.ClearAllShortCuts(ActionList1).SetShortCut(...). SetShortCut(..);

Y análogamente


delphi
  1. Linker := ShortcutLinker;
  2. Linker. SetShortCut(..);
  3. Linker.SetShortCut(..)

Simplemente son distintas formas de escribir lo mismo, de forma que quede más claro y evitando repetir lo obvio en cada paso

Espero haberlo explicado bien


Editado por Agustin Ortu, 30 agosto 2016 - 10:32 .

  • 1

#5 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 30 agosto 2016 - 10:24

Hola Elíseo, en realidad se trata de objetos TAction definidos en un TActionList. Simplemente lo que hago es asignarle su atajo de teclado. El efecto conseguido es que Delphi escucha todos los eventos del teclado y cuando detecta un TAction con el ShortCut que corresponde a la tecla presionada, invoca al método execute.

 

Esa en particular es irónica porque se cerraría la ventana o form antes de ejecutar la acción. Pero puede ser divertido decirle a algún usuario que una nueva función se agregó al sistema y que para verla sólo tiene que pulsar alt f4


  • 1

#6 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.269 mensajes
  • LocationArgentina

Escrito 31 agosto 2016 - 11:25

Recién puedo entrar al foro, estuve toda la mañana en el curso.

 

Ahora lo entiendo mejor. Gracias, y disculpa por hacerte explicar en detalles tu propuesta a las 1am mientras yo ya estaba tirado en la cama  :D 

 

Saludos,


  • 1

#7 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.012 mensajes
  • LocationMéxico

Escrito 31 agosto 2016 - 11:43

Hola Elíseo, en realidad se trata de objetos TAction definidos en un TActionList. Simplemente lo que hago es asignarle su atajo de teclado. El efecto conseguido es que Delphi escucha todos los eventos del teclado y cuando detecta un TAction con el ShortCut que corresponde a la tecla presionada, invoca al método execute.

 

Esa en particular es irónica porque se cerraría la ventana o form antes de ejecutar la acción. Pero puede ser divertido decirle a algún usuario que una nueva función se agregó al sistema y que para verla sólo tiene que pulsar alt f4

 

Entiendo, pensé que estaba podría ser una alternativa para prevenir esa combinación de teclas :)

 

Saludos


  • 1

#8 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 31 agosto 2016 - 12:13

No hay problema, estaba sufriendo del conocido problema de "agarre el celular en la cama y no me deja dormir" :)


  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.269 mensajes
  • LocationArgentina

Escrito 31 agosto 2016 - 01:13

No hay problema, estaba sufriendo del conocido problema de "agarre el celular en la cama y no me deja dormir" :)

 

Vaya, yo hago lo mismo. Me acuesto y estoy con el celu viendo los foros, el correo, Twitter y/o Facebook un rato.

 

Si no fuera porque usas los helpers, genéricos e interfaces mucho más que yo... juraría que son mi clon... tu tampoco usas data-ware, usas patrones cuando puedes,  eres analista y  hasta que no tienes estudiado el tema no metes código...

No hay otra, o sos un clon o es que finalmente ha sucedido lo que más temía: ¡Una de mis personalidades ha logrado salir de la matrix y cobró vida propia! :D

 

Saludos,


  • 1

#10 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 31 agosto 2016 - 09:14

Si no fuera porque usas los helpers, genéricos e interfaces mucho más que yo... juraría que son mi clon... tu tampoco usas data-ware, usas patrones cuando puedes,  eres analista y  hasta que no tienes estudiado el tema no metes código...
No hay otra, o sos un clon o es que finalmente ha sucedido lo que más temía: ¡Una de mis personalidades ha logrado salir de la matrix y cobró vida propia! :D

 
Jaja lo que van a sufrir los que tengan que leer todas las locuras que ponemos. Es muy cierto que hay muchas cosas que compartimos, incluso algunos habitos muy raros  8-|
 
--

Siguiendo con el tema me esta gustando esto de la fluides. Creo que me voy a poner en Status = Superfluido  (h)
 
Por ejemplo hoy se me ocurrio (para seguir divirtiendome un rato) escribir otra interface fluent para rellenar TPopupMenus

La idea es que hay una muy sencilla interface IAction que tengo dando vueltas por ahi hace rato y tiene algunas propiedades (Caption, ShortCut, y Enabled creo que son las mas pertinentes, el resto son cosas de logica de negocios) y unos metodos BeforeExecute y Execute

Bueno, el resultado fue escribir cosas como esta:
 


delphi
  1. procedure TForm1.CreateForm(Sender: TObject);
  2. var
  3. Action: IAction;
  4. begin
  5. Action := {* ... crear este objeto ... *}
  6. OverflowMenu
  7. .AddAction(Action)
  8. .AddSeparator
  9. .AddCustomAction
  10. .WithShortCut('A', [ssCtrl])
  11. .WithCaption('&Agregar usuario')
  12. .OnExecute(
  13. procedure
  14. begin
  15. // ...
  16. end)
  17. .OnBeforeExecute(
  18. procedure
  19. begin
  20. // ...
  21. end)
  22. .Menu { vuelvo a obtener la referencia "original", porque dentro de la cadena de mensajes
  23.   al invocar a AddCustomAction, cambio el tipo de retorno a otra interface que es la
  24.   que tiene los metodos WithCaption, WithShortCut, OnExecute }
  25. .AddSeparator
  26. .AddCustomAction
  27. .WithCaption('&Eliminar usuario') // puedo invertir el orden si quisiera
  28. .WithShortCut('E', [ssCtrl])
  29. .OnExecute(
  30. procedure
  31. begin
  32. // ...
  33. end);

En las primeras lineas se puede ver como se crea un objeto y se agrega en la coleccion, hasta ahi nada del otro mundo; lo unico que puede verse que cambia es que inmediatamente puedo invocar a AddSeparator
 
Mientras escribia el codigo se me ocurrio que podia ser util y ademas un nuevo desafio lograr lo que se ve en las lineas que sigen
 
La idea es poder crear acciones "on-the-fly", o "al vuelo", sin tener que andar implementando la clase. Al mismo tiempo tiene que ser compatible con la sintaxis fluida y la interface, es decir, necesito que implemente IAction con todos sus metodos y propiedades, y de alguna manera proveer un mecanismo en este armado "al vuelo" para poder asignar esos valores
 
El secreto esta, en como dice el comentario, cambiar el tipo de retorno
 
Paso a dejar la declaracion de las interfaces:
 


delphi
  1. {$REGION 'IMenuItemAction'}
  2. /// <summary> Extiende IAction con algunas caracteristicas para interfaz grafica </summary>
  3. IMenuItemAction = interface(IAction)
  4. ['{7F7F5CFB-CA1E-4D2F-82E3-509791264ECB}']
  5. function GetCaption: string;
  6. function GetEnabled: Boolean;
  7.  
  8. procedure SetCaption(const Value: string);
  9. procedure SetEnabled(const Value: Boolean);
  10.  
  11. /// <summary> El texto que se muestra en el menu </summary>
  12. property Caption: string read GetCaption write SetCaption;
  13. /// <summary>
  14. /// Determina si la accion esta habilitada o deshabilitada
  15. /// True: La accion esta habilitada y se puede ejecutar
  16. /// False: La accion esta deshabilitada y no se puede ejecutar
  17. /// </summary>
  18. property Enabled: Boolean read GetEnabled write SetEnabled;
  19. end;
  20. {$ENDREGION}
  21.  
  22. ICustomMenuItemAction = interface;
  23.  
  24. {$REGION 'IOverflowMenu'}
  25. /// <summary> Administra una coleccion de acciones que se muestran en un menu desplegable </summary>
  26. IOverflowMenu = interface
  27. ['{7351FD4D-EEC8-4035-BD6B-C84148A61BE0}']
  28. function GetActions: Spring.Collections.IEnumerable<IMenuItemAction>;
  29. function GetIsEmpty: Boolean;
  30.  
  31. /// <summary> Agrega una accion al menu en donde su metodo Execute se implementa con un metodo anonimo </summary>
  32. function AddCustomAction: ICustomMenuItemAction;
  33. /// <summary> Agrega una accion al menu </summary>
  34. function AddAction(const Action: IMenuItemAction): IOverflowMenu;
  35. /// <summary> Agrega una coleccion de acciones al menu </summary>
  36. function AddActions(const Actions: TArray<IMenuItemAction>): IOverflowMenu; overload;
  37. /// <summary> Agrega una coleccion de acciones al menu </summary>
  38. function AddActions(const Actions: Spring.Collections.IEnumerable<IMenuItemAction>): IOverflowMenu; overload;
  39. /// <summary> Agrega un separador en la posicion indicada por Index; si Index es < 0 lo agrega al final </summary>
  40. function AddSeparator(Index: Integer = -1): IOverflowMenu;
  41. /// <summary> Elimina la Accion del menu </summary>
  42. function RemoveAction(const Action: IMenuItemAction): IOverflowMenu;
  43. /// <summary> Elimina todas las acciones del menu </summary>
  44. function ClearActions: IOverflowMenu;
  45.  
  46. /// <summary> Muestra/despliega el menu </summary>
  47. procedure DisplayMenu;
  48. /// <summary> Oculta/cierra el menu </summary>
  49. procedure HideMenu;
  50.  
  51. /// <summary> Coleccion de acciones que contiene este menu </summary>
  52. property Actions: Spring.Collections.IEnumerable<IMenuItemAction> read GetActions;
  53. /// <summary> Devuelve True si el OverflowMenu esta vacio; no tiene en cuenta los separadores </summary>
  54. property IsEmpty: Boolean read GetIsEmpty;
  55. end;
  56. {$ENDREGION}
  57.  
  58. {$REGION 'ICustomMenuItemAction'}
  59. /// <summary>
  60. /// Usada para crear acciones sin necesidad de implementar IMenuItemAction
  61. /// Estas acciones se crean dinamicamente usando fluent interface
  62. /// El metodo Execute se define mediante un metodo anonimo
  63. /// Crear este tipo de acciones usando IOverflowMenu.AddCustomAction
  64. /// </summary>
  65. ICustomMenuItemAction = interface(IMenuItemAction)
  66. ['{C7882155-D3AB-44D9-83BD-2EECE9BC7C19}']
  67. function GetMenu: IOverflowMenu;
  68.  
  69. /// <summary> Setea la propiedad ShortCut de la accion </summary>
  70. function WithShortCut(const Key: Char; const Shift: TShiftState): ICustomMenuItemAction; overload;
  71. /// <summary> Setea la propiedad ShortCut de la accion </summary>
  72. function WithShortCut(const Key: Word; const Shift: TShiftState): ICustomMenuItemAction; overload;
  73. /// <summary> Setea la propiedad ShortCut de la accion </summary>
  74. function WithShortCut(const Value: TShortCut): ICustomMenuItemAction; overload;
  75. /// <summary> Setea la propiedad Caption de la accion </summary>
  76. function WithCaption(const Value: string): ICustomMenuItemAction;
  77. /// <summary> Setea el metodo anonimo que implementa el metodo Execute de la accion </summary>
  78. function OnExecute(const Value: TProc): ICustomMenuItemAction;
  79. /// <summary> Setea el metodo anonimo que implementa el metodo BeforeExecute de la accion </summary>
  80. function OnBeforeExecute(const Value: TProc): ICustomMenuItemAction;
  81.  
  82. /// <summary> Referencia al Menu que contiene la accion. Permite volver a la cadena de metodos fluent </summary>
  83. [Weak] property Menu: IOverflowMenu read GetMenu;
  84. end;
  85. {$ENDREGION}

 
 
 
 
Como se puede ver, la primera interface define el protocolo de las acciones que puede "ejecutar" el menu
 
Luego IOverflowMenu es quien administra todo eso y se encarga de presentar todo al usuario en algun componente. Como me gusta mantenerme en lo abstracto decidi no "atar" a ningun componente en particular, y dejar que la implementacion se encarge del trabajo sucio. 
 
Como en el primer post, el secreto es el mismo, los metodos que normalmente serian procedimientos sin valor de retorno, ahora son funciones que siempre devuelven Self
 
Luego hay una tercera interface, ICustomMenuItemAction, que es la que hace posible que se puedan crear las acciones como en el codigo de ejemplo que mostre arriba
 
Como se ve, extiende a la interface IMenuItemAction que es la que "entiende" el menu; sino, el codigo ni compilaria

 

La interface de mas arriba en su jerarquia, estan basadas en propiedades con sus getter y setter como cualquier cosa "normal" en Delphi. Es decir tenemos propiedades como Caption y su getter GetCaption. El retorno de GetCaption es un string, y yo necesito seguir enviandole mensajes al mismo objeto; y las propiedades como SetCaption, son procedimientos, no admiten valor de retorno, por lo que tampoco sirven

 

Entonces el truco es ese, el metodo AddCustomAction en lugar de retornar el menu, crea y devuelve un objeto que implementa ICustomMenuItemAction, y a ese si que puedo mandarle otros mensajes. Ahi es donde estan definidos WithCaption, WithShortCut, etc. Pareceria que estoy duplicado codigo,, parece que si porque esos metodos lo unico que hacen es asignar el valor usando las propiedades heredadas, pero ojo, retornan Self; y entonces puedo seguir con la cadena

 

La propiedad Menu es la que me permite volver a la referencia original para seguir agregando cosas al menu. Es como decir, listo termine con esta accion custom, ahora quiero seguir agregando mas al menu

 

Y finalmente para que la accion haga algo, necesito proveerla de funcionalidad.. pero no estoy escribiendo una clase para dicha accion, como hago para que cuando se invoque su metodo Execute "pase algo"? Y la respuesta fueron los metodos anonimos

 

Los metodos anonimos no son mas que un puntero a una funcion/procedimiento. Se pueden usar como cualquier variable, es decir, se pueden almacenar en una variable de instancia por ejemplo; y se puede invocar a su codigo cuando se de la gana

 

Basicamente, en AddCustomAction, se crea una clase que implementa todas las interfaces necesarias, y de hecho los metodos Execute y BeforeExecute estan implementados asi:


delphi
  1. TCustomMenuItemAction = class(TInterfacedObject, IAction, IMenuItemAction, ICustomMenuItemAction)
  2. strict private
  3. [Weak] FMenu: IOverflowMenu; // para devolver en el metodo GetMenu
  4. FExecuteProc: TProc; // TProc esta definido en SysUtils asi: reference to procedure
  5. FBeforeExecuteProc: TProc; // osea que se puede almacenar en el cualquier procedimiento
  6.  
  7. procedure Execute;
  8. procedure BeforeExecute;
  9. public
  10. constructor Create(AMenu: IOverflowMenu);
  11. property ExecuteProc: TProc read FExecuteProc write FExecuteProc;
  12. property BeforeExecuteProc: TProc read FBeforeExecuteProc write FBeforeExecuteProc;
  13. end;
  14.  
  15. function TCustomMenuItemAction .WithCaption(const Value: string): ICustomMenuItemAction;
  16. begin
  17. SetCaption(Value);
  18. Result := Self;
  19. end;
  20.  
  21. procedure TCustomMenuItemAction.BeforeExecute;
  22. begin
  23. if Assigned(BeforeExecuteProc) then
  24. BeforeExecuteProc();
  25. end;
  26.  
  27. procedure TCustomMenuItemAction.Execute;
  28. begin
  29. BeforeExecute;
  30.  
  31. if Assigned(ExecuteProc) then // el check por nil es necesario
  32. ExecuteProc(); // -> los parentesis son necesarios para decir que estamos "invocando"
  33. end;

Como se ve no es tan complicado

 

En fin puedo concluir que la programacion fluida puede resultar muy util para algunos casos; aumenta enormemente la legibilidad del codigo y evita repetir partes del mismo; por lo pronto, no se repite todo el tiempo el objeto al que le enviamos el mensaje

Tambien creo que brilla mas si se usa para trabajar con ciertas estructuras de datos; he visto fragmentos de codigo que trabajan con nodos xml, arboles, grafos. En esos casos su sintaxis minimalista, limpia y clara, con ese sangrado para separar niveles, aporta bastante a la legibilidad

 

Pero como todas las cosas tiene sus contras. La peor es que en realidad por mas que escribamos 100 lineas fluidas, para el programa es una sola. Me explico mejor, si yo pongo un punto de ruptura en la primera linea y luego doy F8, el codigo pasa a la linea siguiente al final de la parte fluida; es decir, a nivel de depuracion, es bastante engorroso. No se pueden poner puntos de ruptura en todo ese "treeview" de invocaciones encadenadas de metodos

 

La segunda contra esta relacionada con la performance. En cada invocacion a cada metodo estamos activando el conteo de referencias de interfaces, lo cual no tiene sentido y como para colmo la implementacion de TInterfacedObject (que supongoque usamos todos para implementar interfaces :)) es thread-safe, usa TInterlocked para incrementar referencias y eso obviamente es mas lento. Este punto es mas subjetivo ya que solo tendria sentido en casos como los mencionados procesar grandes archivos xml u otra estructura de datos inmensa. 

 

A ver si alguien se anima a inventar alguna cosa rara y superfluida!

 

Saludos


Editado por Agustin Ortu, 31 agosto 2016 - 09:17 .

  • 1

#11 sir.dev.a.lot

sir.dev.a.lot

    Advanced Member

  • Miembros
  • PipPipPip
  • 545 mensajes
  • Location127.0.0.1

Escrito 02 septiembre 2016 - 05:46

Esta muy Interesante el tema. (y) (y) (y) 

 

Saludos!


  • 0

#12 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.269 mensajes
  • LocationArgentina

Escrito 02 septiembre 2016 - 07:06

Tu propuesta me suena un tanto similar a lo que propone el patrón Tuberías y Filtros, o mejor conocido como Pipes and Filters o simplemente Pipeline.

Quizá te resulte de interés darle una mirada.

Si bien no lo estudié en mucha profundidad, y admito que debería darle algún momento la seriedad con que estudié otros patrones... hay dos artículos que encontré y me gustaron por su simplicidad al momento de explicarlo:

El 2do no tiene desperdicio, muy bien presentado.

 

Saludos,


  • 2

#13 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 06 septiembre 2016 - 06:36

Saludos.

 

Este estilo de programación lo utiliza Primož Gabrijelčič en su librería OmniThreadLibrary. 


  • 2

#14 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 06 septiembre 2016 - 10:30

Toda la razon Rolphy, Omni es una gran biblioteca. Yo apenas la he usado para el Async.Await :)

 

Los articulos parecen interesantes Marchelo. El tema de los pipe son cosa mucho mas antigua creo, en Linux se pueden crear justamente pipes y encadenar comandos (la salida de un comando se usa como entrada de otro) que es, como decis, muy similar a lo que estamos discutiendo

 

Estoy experimentando con cosas de este estilo:


delphi
  1. function TForm1.ExecuteQuery: IConsultaFacturasVenta;
  2. begin
  3. Result := ConsultadorFacturas
  4. .WhereDate(FechaDesde, FechaHasta)
  5. .WherePuntoVenta(PuntoVenta)
  6. .WhereTipos(TiposComprobante)
  7. .WhereNumero(Numero)
  8. .WhereCliente(IdCliente)
  9. .WhereVendedor(IdVendedor)
  10. .WhereNroPlanilla(NroPlanilla)
  11. .WhereEstados(EstadosComprobante)
  12. .WhereBorrada(Borradas)
  13. .Query;
  14. end;

Saludos


Editado por Agustin Ortu, 06 septiembre 2016 - 10:31 .

  • 1

#15 sir.dev.a.lot

sir.dev.a.lot

    Advanced Member

  • Miembros
  • PipPipPip
  • 545 mensajes
  • Location127.0.0.1

Escrito 20 septiembre 2016 - 11:20

Hola.

 

A los que esten acostumbrado a usar JAVA... estarian de acuerdo conmigo, si escribiera que este estilo tambien se parece a:

 

https://en.wikipedia...Method_chaining

 

Algo de esto en Delphi / Object Pascal.


delphi
  1. Method chaining can be expressed in ObjectPascal as follows:
  2.  
  3. interface
  4. TPerson = class
  5. private
  6. FName: string;
  7. FAge: Integer;
  8. public
  9. function name(const aName: string): TPerson;
  10. function age(const anAge: Integer): TPerson;
  11. function introduce(): TPerson;
  12. end;
  13.  
  14. implementation
  15.  
  16. function TPerson.age(const anAge: Integer): TPerson;
  17. begin
  18. self.FAge := anAge;
  19. Result := self;
  20. end;
  21.  
  22.  
  23.  
  24. function TPerson.introduce: TPerson;
  25. begin
  26. ShowMessage(Format('Hello, my name is %s and I am %d years old.', [FName, FAge]));
  27. Result := self;
  28. end;
  29.  
  30.  
  31. function TPerson.name(const aName: string): TPerson;
  32. begin
  33. self.FName := aName;
  34. Result := self;
  35. end;
  36.  
  37.  
  38. initialization
  39. TPerson.Create().name('Mohamed').age(49).introduce().Free;
  40.  
  41. end.

Saludos!


  • 1

#16 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.269 mensajes
  • LocationArgentina

Escrito 10 abril 2017 - 09:36

Necesito hacer este comentario off topic. Hoy me he topado con un recuerdo de facebook... Y al leerlo no pude evitar recordar lo que comenté en mi último mensaje en este hilo.

 

Demasiada casualidad de que mi clon Salteño se llame igual que mi clon de La Plata. :o ¡Recién caigo! Me había olvidado de que ya tenía un clon... ¿No te habrás mudado de acá para allá Agustín de La Plata? ¡Con razón no querías mostrar tu cara en el video de la entrevista! Se iba a confirmar mi teoría. :D

 

Ahora, un poco de fuera de bromas, para ponerlos en contexto:

Resulta ser que muchos de mis compañeros, amigos y conocidos me han dicho durante tiempo que me suelen ver en lugares a los que no suelo ir con frecuencia, que me saludan a la distancia y los miro como si no les reconociera ni devuelvo gentilezas.

Les comento que están confundidos... que no puede ser, que no tengo costumbre de ir por esas zonas de la ciudad.

Así fue hasta que hace dos años al ir a la Sandwichería que esta cerca de casa una persona que estaba ahí me ve y me llama por Agustín, que tal me iba en la tienda del shopping. Le digo que se equivoca de persona, se disculpa, me mira de nuevo y me dice "No jodas. Sos Agustín. Sos idéntico". Intenté sacarle más info a esa persona pero justo agarraba su pedido y no he vuelto a verla.

Hasta el momento no he podido encontrar al tan famoso Agustín.

 

Saludos,

Archivos adjuntos


  • 1

#17 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 10 abril 2017 - 11:10

Como estan las cosas.. no se que seria peor!! tener dos Agustines o dos Marcelos!! Que dios nos libre y nos guarde  :D  :D  :D

 

Como nos sigamos replicando vamos a terminar dominando el mundo como los agentes Smith! 

 

Eso si, quien sabe cual es el "original"  :dlaug:


  • 0