Próxima cita el 26 de Febrero

febrero 8, 2010 en Advertencia, Delphi, Entrada Diario, Eventos, Noticias, Noticias Delphi, Recordatorio, Seminario web

No voy a hacerlo demasiado largo, ya que es un tanto tarde (madrugada del lunes en España) y como todo hijo de vecino tengo que levantarme temprano para trabajar, pero tenía pendiente comentar con vosotros que ya han confirmado la fecha del evento “Delphi Developer Day IV”, que va especialemente dirigido a la comunidad hispana: viernes, 26 de Febrero (2010).

En lo que respecta a la hora de inicio, según el formulario de respuesta que se lanza al registrarte al evento, figura las 7:00 AM (PST), que si no me equivoco y de acuerdo al último que pude asistir, vendrán a ser sobre las 4 pm (en España).

Recordad que en http://dn.embarcadero.com/article/40336 disponéis de mas información tanto del contenido del seminario web como de los expertos que van a participar. Os adelanto simplemente que serán 8 presentaciones, a la cual mas interesante  :-)

No deberías de perdértelo.

Por cierto: esta tarde no he podido dedicar tiempo al blog ya que estoy bastante absorbido con el aprendizaje de punto net y mi toma de contacto con Delphi Prism. Y al final, tienes que combinar lo mejor que se puede el tiempo de forma que en este caso, desgraciadamente el blog ha sido el sacrificado. No obstante, no quería acabar el día sin al menos dejar estas letras.

Ha sido una tarde tranquila, recreandome y “jugando” con el entorno, e intentando poner en práctica el ejemplo de desarrollo que propone Marco Antonio Santín en su video del “Curso de Desarrollo de aplicaciones de escritorio y acceso a datos con Delphi Prism”. A medida que voy siguiendo los pasos, comparo mentalmente con las estructuras que uso habitualmente desde mis desarrollos y amplío las ideas comentadas, intentando encontrar puntos similares, que me sirvan de referencia.

Así por ejemplo, he intentado encontrar mis clases Ancestro y Browser como base de las propuestas del curso y he acabado traduciendo la llamada principal “Mostrar” a punto net, para ver que tal quedaba. (Mostrar era un método de clase de la clase base TAncestro).

Tras un buen rato, el método original

class function TAncestro.Instancia: TAncestro;
var
  I: Integer;
begin
  for I := Screen.FormCount - 1 downto 0 do
  begin
    TForm(Result) := Screen.Forms[I];
    if Result.ClassType = Self then Exit;
  end;
  Result := nil;
end;
class function TAncestro.Mostrar: TAncestro;
begin
  Result := Instancia;
  if Assigned(Result) then
  begin
    if Result.WindowState = wsMinimized then
      ShowWindow(Result.Handle, SW_RESTORE);
    Result.BringToFront;
  end
  else
  begin
    Result := Create(Application.MainForm);
    Result.RegistroLeer;
    Result.Show;
  end;
end;

quedaba reescrito como:

class method TAncestro.Instancia(FormType: System.Type): TAncestro;
begin
   Result:= Nil;
   for each f: Form in Application.OpenForms do
   begin
      if (f.GetType() = FormType) then
      begin
        Result:= TAncestro(f);
        Exit;
      end;
   end;
end;

class method TAncestro.Mostrar(FormType: System.Type): TAncestro;
begin
  Result:= Instancia(FormType);
  if Assigned(Result) then
  begin
    if Result.WindowState = FormWindowState.Minimized then Result.Show;
    Result.BringToFront;
  end
  else begin
    var Prototipo: Object := Activator.CreateInstance(FormType);
    Result:= TAncestro(Prototipo);
    Result.MdiParent:= ActiveForm;
    Result.Show;
  end;
end;

Y la llamada desde el menu principal había cambiado también. Inicalmente escribía:

TModelos.Mostrar;

y ahora, tenía que añadir algunas lineas mas (aunque no demasiadas más). En la primer aproximación hacia algo como:

  var fModelo: TModelos := New TModelos();
  var Prototipo: System.Type;
  Prototipo := fModelo.GetType();
  TModelos.Mostrar(Prototipo);

Luego me he dado cuenta de que no tenia demasiado sentido instaciar dos veces el objeto por lo que tras otro buen rato, escribo finalmente:


  var Prototipo: System.Type;
  Prototipo := System.Type.GetType("Empresa12.TModelos");
  TModelos.Mostrar(Prototipo);

Lo cual parece mas razonable y ademas funci0na…  :-)

Como primer punto de encuentro está bien (imagino que en próximos días encontraré formas de simplificar esas llamadas)  y doy por bien empleada la tarde. Pero es difícil evitar sentirse extraño y no hacer algunas comparaciones, de cosas que he podido echar en falta. Por ejemplo en el editor de código: Es imposible no darse cuenta de que el editor no es mas cómodo ni usable por tener una pestaña adicional (una para el código y otra para el diseñador del formulario). Así pues, mientras Delphi trabajaba entre bastidores, me he encontrado con sorpresa que algunos cambios manuales en el nombre de la clase o del ascendente en el tipo, ocasionaba errores de lectura en la carga del formulario en tiempo de diseño, fruto de que no se habian modificado los modulos que llevan la referencia “XXX.designer.pas”. Y he tenido que ir repetidamente a ese modulo de diseño de formulario y abrirlo con un editor de texto plano y modificar manualmente los nombres para que fueran correctos y se solucionara el error. :-(  ¡Cosas un tanto extrañas que te hacen el trabajo mas farragoso!

Tampoco entiendo el por qué no han puesto un filtro en la paleta de componentes de forma que puedan ser fácilmente encontrados a la hora de buscar uno en concreto. Delphi lo resolvió en las ultimas versiones, tanto a nivel de la paleta de componentes como de busquedas mas generales a nivel de ide y opciones de configuración.

Existen algunos detalles mas pero parece aconsejable dejarlos para otro día dada la hora que es…  :-)

Mañana será otro día. Por hoy yo tengo bastante…


Release Notes: Delphi and C++Builder 2010 Updates 4, 5 (Database Pack), and Boost Library

diciembre 16, 2009 en Advertencia, Ayuda, Consejo, Delphi, Entrada Diario, Noticias, Noticias Delphi

Han llegado los primeros comentarios de la disponibilidad de los últimos updates para Delphi 2010, C++ Builder 2010 y Rad Studio. Yo lo he podido leer por el comentario de Andreano Lanusse desde el grupo Twibes Hispano y tal y como nos dice, podeis acceder facilmente al mismo tan solo con la opción de chequear actualizaciones (Check for Updates) existente en el item creado por el entorno en el menu de inicio.

Logicamente, tambien podeis acceder a la descarga a través del registro en la zona de descargas de Embarcadero.

Es muy importante que leais las notas de instalación antes de efectuar la actulización. Estas notas las podeis leer en la entrada de Kris Houser

http://edn.embarcadero.com/article/40174#14KnownIssues

 No dejeis de leerla, ya que existen algunos prerrequisitos que afectan al orden de instalación, tanto de los updates que contiene (segun el producto) como respecto a los anteriores, que puedan estar pendientes de instalación.

En mi caso concreto, parece ser que finalizó con exito… pero he podido ver durante la actualización de la instalación del update 5, numerosas advertencias similares a la que se muestra en la imagen:

Advertencia instalacion databasepack (Update 5)

Que permitieron seguir la instalación y como comento, aparentemente finalizar con exito…

Ni idea… :-(

El problema de estas advertencias es que no sabes tras aceptar, si han sido “resueltas” por el mismo instalador o ha quedado algo incorrecto.  :-)   

En el menú de ayuda del entorno, se visualizan los updates pero supongo que tendré que buscar la forma de asegurarme de que realmente la instalación fue correcta. Lo peor de todo es que en las notas de instalación, se comenta que la desistalación de estos updates puede ser problematica… Por eso os comento que leais con atención las notas de instalación.

__Uninstalling Update 4
If you need to uninstall Update 4, you must uninstall the entire product and reinstall one of the following:
  • Delphi 2010, C++Builder 2010, or RAD Studio 2010 (RTM)
  • Update 1 to Delphi 2010, C++Builder 2010, or RAD Studio 2010

    Uninstalling Update 5 (Database Pack)

If you need to uninstall the update, select “Embarcadero Delphi and C++Builder 2010 Database Pack” in Add/Remove Programs, and click Remove. The IDE has dependencies on the Database Pack, so you should not uninstall the DataBase Pack if you will continue to use the IDE.

If you have all three personalities of RAD Studio 2010 installed (including Delphi Prism), uninstalling Update 5 (Database Pack) or uninstalling Delphi Prism will mean that you must restart the Blackfish SQL server.

    Uninstalling the Boost Library

The C++ Standard Library has dependencies on Boost, so you should not uninstall Boost if you will continue to use the C++ compiler.
 

Y por ultimo, quizás dejar el comentario que me ha causado extrañeza que tras finalizar la actualización, fuera necesario registrar de nuevo el producto.

En fin… dejo el comentario por si quereis añadir algo. Ya sabeis que sois libres de añadir lo que gusteis.

Lo mas destacado… :-)

junio 14, 2009 en Advertencia, ¿Sabías que...?, ¿Sabías que...? [Delphi], Consejo, Delphi, Enlace interesante, Entrada Diario

Suelo reservar la tarde del domingo, un rato al menos, para hacer un repaso mental de lo que me ha parecido mas interesante a nivel de enlaces durante toda la semana. Quizás, antes -hablo de bastantes meses atrás- dedicaba mas tiempo a buscar paginas nuevas,  o entradas nuevas, si fuera el caso de que fueran conocidas y ya las hubiera incluido en mis enlaces, pero ahora, desde que empecé a incorporar los breves mensajes de Twitter (o Twibes) la situación cambió, y en lugar de mi busqueda tradicional, me he concentrado en seleccionar los comentarios que tuvieran mas interés.

El domingo anterior ya lo hice. Y si os dais cuenta, de no hacerlo así, muchos de esos comentarios pueden llegar a perderse ya que quedaran sepultados por los nuevos mensajes y por desgracia, algo como “voy a pasear al perro” en respuesta de “que es lo que estas haciendo” o “voy a trabajar duramente”, será inevitablemente el ejecutor de que no acaben siendo visibles y se oculten por los siglos de los siglos. Eso si algun dia NO deja de existir ese invento de las url cortas y nos dejan sin una referencia real a la que apuntar (ni forma de acceder) :-)

Huele que hay algo en esta nueva concepción de internet que no me acaba de gustar del todo. Recuerdo cuando en los foros tradicionales, donde recibiamos un correo en respuesta de la pregunta de un compañero, el moderador -yo en las ocasiones en las que lo he sido- era capaz de “recriminar” que un compañero X se fuera por los cerros de ubeda y nos contara su vida. Y eso era así porque los foros, en aquellos momentos, tenían un cariz mas “profesional” o al menos se entendía así. Era otro momento, y entiendase el recriminar, sin connotación negativa puesto que el principal objetivo de aquellos foros era basicamente apoyar al programador en sus tareas cotidianas.

Sin embargo, los años y la necesidad de marcar nuevas directrices y de llamar de otra forma a lo que ya existía, nos han hecho necesitar virtualmente conocer algo mas de los interlocutores y así, podemos llegar a saber que nuestro autor favorito un día se rompió el brazo o detalles mas escatológicos sobre su vida privada (dicho esto entre comillas). :-) Estamos casi obligados a tener un perfil repartido entre el facebock, twitter, los spaces de microsoft, blogger, wordpress, tuenti, etc… para existir en el mundo virtual.

Ya se que me repito. Pero son cosas que nos afectan. La servidumbre de asimilar estas nuevas ideas dentro de los blogs tienen costes adicionales. Yo por ejemplo, pagué ayer con una hora de sueño, cuando a las 2 de la mañana, acabando la entrada del blog y en el momento de hacerla publica, tras varias horas editandola, subiendo al servidor las imagenes, el codigo, revisando los errores… me di cuenta de que no se podía visualizarla. ¿Y por qué razon? Pues porque uno de los complementos o pluggins de wordpress generaba un error e impedía que se cargara correctamente la pagina. Así que me costó una hora descubrir que lo que realmente fallaba era el pluggin y localizarlo para intentar resolver el problema.

Hace poco, no fue una hora sino una tarde y por otro motivo similar…

Todo no es malo. No. Muchos de los mensajes contienen referencias a entradas que posiblemente nos pasarían inadvertidas, no porque no tuvieran importancia sino porque uno al final visita las paginas que conoce y dificilmente sale de su entorno.

Podriamos llamar a esta sección “And the winner is…”. Esta semana me quedo con la entrada http://alex.ciobanu.org/?p=232 de  Alexandru Ciobanu, en la que abordaba la posibilidad de extender la instancia de TObject y referenciar a una estructura o a otro objeto, que pueda tener un sentido dentro del dominio de nuestra aplicación.

Y me he decantado por esta (habia otra que hacia referencia al futuro de Delphi http://www.theregister.co.uk/2009/06/12/embarcadero_codegear_tools_future/) porque me ha permitido descubrir algunos detalles que desconocía.

* Que el tamaño en bytes de las instancias de TObject, fue modificado en Delphi 2009, pasando a 8 bytes en lugar de los 4 tradicionales. Sobre este punto encontre un artículo que introducía muy bien el tema y lo explicaba, y de paso, me permitía conocer la existencia de un puntero escondido a una nueva instancia de la clase TMonitor (que supuestamente era un tipo clase para sincronizar y facilitar los accesos concurrentes de los hilos de ejecución). O al menos entendí algo similar.

Así que una entrada me llevó a la otra: http://blogs.teamb.com/craigstuntz/2009/03/25/38138/ (Craig Stuntz) donde la información era mucho mas detallada sobre este punto, que habia levantado preguntas en algunos foros sobre el paso de 4 bytes a 8. (http://stackoverflow.com/questions/679022/what-data-does-a-tobject-contain)

Y tambien accidentalmente descubrí al hilo de todas estas investigaciones de sabueso, algunas entradas que comentaban aspectos de la VMT (la tabla de métodos virtuales)

Un del año 2000:  http://oreilly.com/catalog/delphi/chapter/ch02.html

y otra mas actual, que contiene unas imagenes muy directas para comprender que es lo que realmente está sucediendo al instaciarse TObject, entre bastidores.

http://pages.cs.wisc.edu/~rkennedy/vmt

Nada más.

Que tengais una buena semana.

:-)

Experimentos, reflexiones y otros artefactos (IV)

junio 7, 2009 en Advertencia, Artículos, Ayuda, Código, Delphi, Entrada Diario

Hace una tarde calurosa y bastante veraniega. En una tarde así, no se puede estar en casa escribiendo una entrada como esta… :-) El sol cae a plomo a las 6 de la tarde, un día de verano, y parece que todo invita a salir a disfrutar de un rato de deporte. Así que con con vuestro permiso :-) inicio la entrada ahora, hasta donde llegue, y la prosigo esta noche si quedara algo por decir.

¿Por donde empiezo? Ummmm…

Lo habíamos dejado en la parte en la que haciamos un pequeño boceto de las dos clases que ibamos a considerar. La clase TGenerador y la clase TAtributo. Eso nos permitía ver como se podían relacionar y a modo de prueba, imaginábamos un método para permitirnos alterar el orden de los mismos, en caso de ser necesario.

Durante esta semana he podido sacer algunos minutos para adelantarme y darle forma a esas dos ideas. Pero no teneis que perder de vista, por favor, que lo importante no es el código en si mismo, que no es mas que un ejemplo cualquiera y puede contener alguna que otra errata al analizarlo con profundidad, sino el hecho mismo de solicitar de cada uno, un ejercicio de análisis previo y reflexión, intentando razonar en términos de clases, en la medida que nos sea posible.

Una imagen vale mas que mil palabras. Este es el ejemplo que preparé para probar la implementación de ambas clases ya con una funcionalidad mayor.

exercicio2

La imagen muestra en la parte superior, 4 atributos que representan los tipos posibles que hemos adminitido. Un atributo combinable (Atributo 2), capaz de seleccionar multiples valores. Un atributo dependiente (Atributo 3), tambien combinable pero que va a permitirnos descartar alguna combinaciones en funcion de una relación imaginaria. Es decir, el caso de que un tipo de atributo solo pueda combinarse con determinados valores de otro atributo. Decirlo es mas complicado que razonarlo.  En el ejemplo, el color ’18′ solo puede ser combinado con los tipos ‘AA’ y ‘AB’.

Nos queda el atributo dependiente, representado en el Atributo 1, que toma siempre el mismo valor. Y el Atributo rellenable de, que toma valores respecto a otro atributo cualquiera combinable y no rellenable. Es el Atributo 4.

Así que al pulsar el boton “Solución”, nos presenta en los dos componentes TMemo, las combinaciones correctas y las descartadas. En este ejemplo, se aceptan 8 combinaciones y se descartan 16, del total de 24 combinaciones posibles. Las ventanas superiores unicamente existen con el motivo de permitirnos visualizar la información que contiene el generador y los atributos de cara a seguir mejor el ejemplo.

Ahora mismo, el desarrollo del ejemplo se va alejando progresivamente de los bocetos de las dos primeras entradas, representadas en el bucle anidado for, intimamente ligado al interfaz.

Podemos ver la declaración de tipos del módulo que contiene las clases protagonistas:


type

  TEstructura = Class(TObject)
    FIndice: Integer;
    FSiguiente: TObject;
  private
    procedure SetIndice(const Value: Integer);
    procedure SetSiguiente(const Value: TObject);
  published
  public
  constructor Create;
  destructor Destroy; override;
  function Situar(AIndice: Integer): TEstructura;
  property Indice: Integer read FIndice write SetIndice;
  property Siguiente: TObject read FSiguiente write SetSiguiente;
  end;

  TAtributo = class;

  TGenerador = Class(TObject)
  private
    FAtributos: TList;
    FCombinaciones: TStringList;
    function GetAtributos(Indice: Integer): TAtributo;
    function GetDescartado(Indice: Integer): Boolean;
    procedure SetDescartado(Indice: Integer; const Value: Boolean);
    function GetCombinaciones(Indice: Integer): String;
   protected
    procedure DoCombinar; virtual;
    procedure DoLimpiarCombinaciones; virtual;
    procedure AssignCombinaciones; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    function Count: Integer;
    procedure SetUpPosition(AAtributo: TAtributo);
    procedure SetDownPosition(AAtributo: TAtributo);
    function GetPosicion(AAtributo: TAtributo): Integer;
    function GetMultiplicador(AAtributo: TAtributo): Integer;
    function GetTotalCombinaciones: Integer;
    function AddAtributo: TAtributo;
    procedure Combinar; virtual;
    procedure VerificarDependencias;
    property Atributos[Indice: Integer]: TAtributo read GetAtributos;
    property Descartado[Indice: Integer]: Boolean read GetDescartado write SetDescartado;
    property Combinaciones[Indice: Integer]: String read GetCombinaciones;
  end;

  TAtributo = class(TObject)
  private
    FGenerador: TGenerador;
    FListaValores: TStrings;
    FCombinaciones: TStrings;
    FCombinable: Boolean;
    FCaption: String;
    FRellenableDe: TAtributo;
    FDependienteDe: TAtributo;
    procedure SetCombinable(const Value: Boolean);
    procedure SetCaption(const Value: String);
    function GetValues(Indice: Integer): String;
    procedure SetValues(Indice: Integer; const Value: String);
    function GetCombinacion(Indice: Integer): String;
    procedure SetRellenableDe(const Value: TAtributo);
    procedure SetDependienteDe(const Value: TAtributo);
    function GetDependencia(Indice: Integer): TEstructura;
    function GetPosicion: Integer;
    { Private declarations }
  protected
    function DoGetMultiplicador: Integer; virtual;
  public
    { Public declarations }
    procedure LimpiarCombinaciones;
    procedure Combinar;
    function GetMultiplicador: Integer;
    function ValuesCount: Integer;
    procedure UpPosicion;
    procedure DownPosicion;
    constructor Create(AGenerador: TGenerador); virtual;
    destructor Destroy; override;
    function AddValue(const AValue: String): Integer; overload;
    function AddValue(const AValue, ADependencia: String): Integer; overload;
    procedure DeleteValue(const AValor: String);
    procedure VerificarDependencias;
    property Combinable: Boolean read FCombinable write SetCombinable;
    property Posicion: Integer read GetPosicion;
    property Caption: String read FCaption write SetCaption;
    property Values[Indice: Integer]: String read GetValues write SetValues;
    property Dependencia[Indice: Integer]: TEstructura read GetDependencia;
    property Combinacion[Indice: Integer]: String read GetCombinacion;
    property RellenableDe: TAtributo read FRellenableDe write SetRellenableDe;
    property DependienteDe: TAtributo read FDependienteDe write SetDependienteDe;
  end;

Descargar

No nos es posible en una entrada explicar todas las relaciones, por varios motivos razonables, entre los que considero el tiempo y el espacio. Así que lo mejor es que os descargueis el codigo fuente y le deis un vistazo.

Se me ocurren algunas ideas que pueden ser interesantes:

* A pesar de funcionar correctamente, o al menos eso creo, el hecho mismo de declarar las 3 clases en la parte pública del interfaz, hacen que queden disponibles para el uso, métodos que por ejemplo pertenen a TAtributo y que tan solo deberían estar disponibles para la instancia de TGenerador, puesto que va a ser esta quien quede intimamente ligada a ella, dado que conoce como deben ser ejecutados. De hecho, para ser formalmente correcto, el “usuario”, que en este caso es el formulario, debería batallar tanto con TGenerador como con la clase TAtributo, pero parece claro que nunca debería poder ejecutar por ejemplo el metodo VerificarDependencias en el atributo.  Y sin embargo, la disposición de las clases y la publicación de los métodos lo facilitan.

* En ocasiones, al pensar en clases aparecen puntos de vista que inicialmente no considerabamos. De hecho, lo que inicialmente era un bucle for anidado, ha sido sustituido por otro razonamiento. ¿Es posible que la clase Generador le pueda decir a cada atributo: Oye majo, que quiero que te combines? Ese es el motivo de que exista un metodo virtual DoCombinar, que se encarga de que cada atributo ejecute su propia expansión de combinaciones…

A eso me refería al comentar que a veces surgen nuevos puntos de vista, mejores o peores, pero distintos. Se me ocurrío razonar al estilo de la cuenta de la vieja. Y generar una lista de las disposiciones individuales de cada uno de los atributos en las combinaciones.

Este es el razonamiento de la vieja.

Supongamos los siguentes pares (Atributos, valores)

Atributo1  > A, B          Atributo2 >  C, D           Atributo3 > E, F

Podemos seguir un patron para combinarlos (el mismo que seguiría el bucle for)

ACE, ACF, ADE, ADF, BCE, BCF, BDE, BDF

Nos permite individualmente obtener una lista de valores

Atributo1 > A,A,A,A,B,B,B,B     Atributo2 > C,C,D,D,C,C,D,D   Atributo3 > E,F,E,F,E,F,E,F

Por eso aparece por algun lado el multiplicador, que no es mas que las veces que tiene que repetirse el valor en la cadena. En este ejemplo en concreto, el Atributo1 tiene un multiplicador de 4, el Atributo2, un multiplicador de 2, y el atributo3 un multiplicador de 1. Y dichos valores, son factibles de ser implementados mediante una sencilla función.

function TGenerador.GetMultiplicador(AAtributo: TAtributo): Integer;
var
  i,j: Integer;
begin
   if (AAtributo.Combinable) and (not Assigned(AAtributo.RellenableDe)) then
   begin
      Result:= 1;
      i:= FAtributos.IndexOf(AAtributo);
      if  (i < 0) then Result:= -1
      else begin
         for j := i+1 to FAtributos.Count - 1 do
            if (TAtributo(FAtributos[j]).ValuesCount = 0) or
                            (not TAtributo(FAtributos[j]).Combinable)    then
               Result:= Result * 1
            else Result:= Result * TAtributo(FAtributos[j]).ValuesCount;
      end;
   end
   else begin
      Result:= 1;
   end;
end;

* La tercera reflexión que ya se desprende de todo lo comentado, es el tiempo que dedicamos a implementar lo mismo. Visto de la primera forma, como razonabamos en las dos primeras entradas, la ganancia de tiempo podría ser considerable (menor tiempo). Este módulo, el que vemos en la entrada actual, tiene mayor trabajo para hacer exactamente lo mismo y quizás fuera una buena razón para considerar que perdemos el tiempo. Eso que deciamos de: -acaba como sea pero acaba (El jefe dixit) suele ser pan para hoy y hambre para mañana. Así que de cuando en cuando, deberíais dar un buen puñetazo en la mesa :-) y poner una silla trabando la entrada de vuestro despacho de forma que sea imposible que nadie os distraiga. ;-)  Por paradojas de la vida, esa misma persona que hoy os pide que acabeis ya el desarrollo como sea, es la misma que mañana os atará de los pulgares en el mástil mayor, si dentro de 6 meses se complica el desarrollo.

Vale… es una exageración bastante malvada pero yo creo que se me entiende. Y el peor enemigo que puede tener vuestro trabajo es que seais vuestro propio jefe… :-)

* Otra cosa que puede extrañar en el codigo, es que aparezca un tercero en discordia (la clase TEstructura). Es algo bastante habitual y ya  lo referia en alguna entrada anterior al hablar de aquel componente Buscador, que dejabamos atras al hablar de los hilos de ejecución. En aquel caso, el tercero en discorida era una implementación del patron iterador, que permitía recorrer la estructura y avanzar. En este caso concreto, me encontré que para poder trabajar con la dependencias, siendo posible que existiera una relación múltiple entre los valores: un color podia pertenecer a varios tipos, pensé que me era necesaria una forma sencilla de recorrerla. En eso me ayuda la clase TEstructura, que apunta a otra Estructura con el valor de relación. De esa forma, puedo buscar si un color pertenece a un tipo y saber si la combinación debe ser descartada o no.

* Y la ultima reflexión que cierra la entrada: la reutilización de nuestro codigo. Ligarnos a nuestros interfaces nos puede permitir reutilizarlos en forma de herencia y en eso ya nos ayuda el IDE de Delphi, al habilitar nuestros formularios en el repositorio de objetos. Sin embargo, al hacerlo así nos obliga a arrastrar no solo las ideas sino las apariencias y eso no es demasiado bueno en todos los casos.

Creo que os lo puedo demostrar… Dadme unos minutos… Pongamos por caso que dos días despues, vamos a formar una peña con el gabinete de programadores para jugar a las quinielas y poder hacernos ricos y dejar de trabajar. :-) WOWWWWW…  Y como somos previsores recordamos que nuestro programa hacía algo similar… De haberme ligado al interfaz, me hubiera sido imposible en este caso concreto reutilizar esa idea de forma sencilla. Sin embargo, nuestro nuevo planteamiento sí me permite mediante controles distintos y buscando una finalidad que no tiene porque coincidir con la original, reutilizar el código escrito.

Esta es la imagen de nuestro nuevo interfaz de ejemplo:

Dadle un vistazo:

azar1x2

Este es el código que ha permitido ampliar la funcionalidad original (por si teneis curiosidad)

:-)

unit main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ButtonGroup, ExtCtrls, StdCtrls, CheckLst, uAtributos;
const

RestriccionFicticia = 100000;

type
  TForm1 = class(TForm)
    chbPartidos: TCheckListBox;
    chb1: TCheckListBox;
    chbx: TCheckListBox;
    chb2: TCheckListBox;
    Label1: TLabel;
    btnCombinar: TButton;
    chbCombinables: TCheckListBox;
    lbxResultados: TListBox;
    Label2: TLabel;
    labCombinaciones: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure chbPartidosClickCheck(Sender: TObject);
    procedure btnCombinarClick(Sender: TObject);
    procedure chb1ClickCheck(Sender: TObject);
    procedure chbCombinablesClickCheck(Sender: TObject);
  private
    { Private declarations }
    FGenerador: TGenerador;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnCombinarClick(Sender: TObject);
var
  i: Integer;
begin
  fGenerador.Combinar;
  lbxResultados.Clear;
  for i:= 0 to fGenerador.GetTotalCombinaciones-1 do begin
    if not fGenerador.Descartado[i] then
      lbxResultados.Items.Add(fGenerador.Combinaciones[i]);
  end;
  labCombinaciones.Caption:= ' Total: ' + IntToStr(lbxResultados.Items.Count);
end;

procedure TForm1.chb1ClickCheck(Sender: TObject);
begin

  case (Sender as TCheckListBox).Tag of
    1: begin
             if (Sender as TCheckListBox).Checked[(Sender as TCheckListBox).ItemIndex] then
                FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].AddValue('1')
             else FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].DeleteValue('1');

          if not FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].Combinable then begin
             chbx.Checked[(Sender as TCheckListBox).ItemIndex]:= False;
             chb2.Checked[(Sender as TCheckListBox).ItemIndex]:= False;
          end;
       end;
    2: begin
          if (Sender as TCheckListBox).Checked[(Sender as TCheckListBox).ItemIndex] then
             FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].AddValue('X')
          else FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].DeleteValue('X');

          if not FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].Combinable then begin
             chb1.Checked[(Sender as TCheckListBox).ItemIndex]:= False;
             chb2.Checked[(Sender as TCheckListBox).ItemIndex]:= False;
          end;
       end;
    3: begin
             if (Sender as TCheckListBox).Checked[(Sender as TCheckListBox).ItemIndex] then
                FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].AddValue('2')
             else FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].DeleteValue('2');

          if not FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].Combinable then begin
             chb1.Checked[(Sender as TCheckListBox).ItemIndex]:= False;
             chbX.Checked[(Sender as TCheckListBox).ItemIndex]:= False;
          end;
       end;
  end;

  if fGenerador.GetTotalCombinaciones > RestriccionFicticia then begin
    (Sender as TCheckListBox).Checked[(Sender as TCheckListBox).ItemIndex]:= False;
    case (Sender as TCheckListBox).Tag of
       1: FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].DeleteValue('1');
       2: FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].DeleteValue('X');
       3: FGenerador.Atributos[(Sender as TCheckListBox).ItemIndex].DeleteValue('2');
    end;

    Raise Exception.Create('No se puede exceder de '+
                           IntToStr(RestriccionFicticia)+' combinaciones...');
  end;

  labCombinaciones.Caption:= 'Total combinaciones ' +
                             IntToStr(fGenerador.GetTotalCombinaciones);

end;

procedure TForm1.chbCombinablesClickCheck(Sender: TObject);
begin
   if not chbCombinables.Checked[chbCombinables.ItemIndex] then begin
     if chb1.Checked[chbCombinables.ItemIndex] then begin
        chb1.Checked[chbCombinables.ItemIndex]:= False;
        FGenerador.Atributos[chbCombinables.ItemIndex].DeleteValue('1');
     end;

     if chbX.Checked[chbCombinables.ItemIndex] then begin
       chbX.Checked[chbCombinables.ItemIndex]:= False;
       FGenerador.Atributos[chbCombinables.ItemIndex].DeleteValue('X');
     end;

     if chb2.Checked[chbCombinables.ItemIndex] then begin
       chb2.Checked[chbCombinables.ItemIndex]:= False;
       FGenerador.Atributos[chbCombinables.ItemIndex].DeleteValue('2');
     end;
   end;

  FGenerador.Atributos[chbCombinables.ItemIndex].Combinable:=
                               chbCombinables.Checked[chbCombinables.ItemIndex];
  labCombinaciones.Caption:= 'Total combinaciones ' +
                              IntToStr(fGenerador.GetTotalCombinaciones);
end;

procedure TForm1.chbPartidosClickCheck(Sender: TObject);
begin
  chbPartidos.Checked[chbPartidos.ItemIndex]:= True;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  fAtrib: TAtributo;
begin
  FGenerador:= TGenerador.Create;

  for i := 0 to chbPartidos.Count - 1 do begin
     chbPartidos.Checked[i]:= True;
     fAtrib:= FGenerador.AddAtributo;
     fAtrib.Caption:= IntToStr(i);
     fAtrib.Combinable:= True;
  end;

  for i := 0 to chbCombinables.Count - 1 do begin
     chbCombinables.Checked[i]:= True;
  end;

end;

end.

Descargar

Creo que por hoy ya esta bien.

Hace un día estupendo. Y también hay que sacar tiempo para el deporte. No todo puede ser trabajo, ¿no? Además, no se si alguna vez comenté que suelo correr con frecuencia, y a lo largo de la temporada intento participar en alguna que otra carrera popular, casi siempre en la distancia Media Maratón. Creo que es importante el equilibrio personal y que debemos buscarlo en nuestro interior.

Quedan más ideas en el tintero. Las compartiremos en próximas entradas.

Experimentos, reflexiones y otros artefactos (II)

mayo 17, 2009 en Advertencia, Artículos, ¿Sabías que...?, ¿Sabías que...? [Delphi], Código, Delphi, Entrada Diario, Taller práctico

Seguimos avanzando en el artículo de la primera parte de la serie.

¿Por donde ibamos? Sí. Ya…

Nuestro formulario iba creciendo. De hecho habíamos incluído una pequeña modificación que invertía el código y el formulario, comenzaba a parecerse -con la prudencia que hace falta para comentar ésto ya que estas lineas son meramente experimentales- a una hipotética herramienta para generar combinaciones de códigos. Esto nos permite tomar una pequeña dosis de realidad en nuestro razonamiento. Una herramienta que iba a crecer pero ligada desgraciadamente a los componentes que iban formando su interfaz.

No. No creais que es algo irreal o fantástico. Aunque no es demasiado habitual, algunas empresas tratan sus artículos como una concreción de las características que contienen los patrones modelos y escogen los escenarios en los que se hace efectivo este paso de materialización. Por ejemplo al ser cursado un pedido por parte del cliente, momento en el que podrían las distintas selecciones de caracteristicas desembocar en la generación de un código único y desencadenar los proceso de alta en artículos e inserción en lineas de detalle de pedido. Y no confundais por favor este código necesariamente con la clave primaria de una tabla cualquiera, entre otras cosas porque no hay nada que impida que puedan coexistir una clave primaria incremental (o no incremental), con cualquier otra combinaciones de claves que acaban formado parte de un índice de clave única.

En este ejemplo, vamos descubriendo algunos detalles sobre la marcha. ¿Eso pasa tambien en los proyectos reales, no es así?. Nos añadiran un mes despues una caracteristica que tambien vamos a codificar con dos caracteres. Con la salvedad de que al parecer no es combinable, como sí ocurría con los dos atributos que ya conocíamos: el color y el tipo. Sera constante y variable para una combinación determinada de color/tipo, bien por selección de una entre una lista asociada a esta, bien por afinidad a uno de los atributos, por lo que hemos convenido en denominar ese comportamiento como ”rellenable de”. Esto que parece muy complejo lo vereis muy sencillo en el código. 

Pero lo mejor es que lo veais en una imagen del formulario, una vez que hemos hecho las modificaciones. Nuestro bucle “for” va creciendo y va anidadandose para generar las combinaciones. Para hacerlo un poco mas real el ejemplo, he añadido varios componentes TClientDataSet, asociados a cada uno de los atributos, y he almacenado localmente los datos en tres ficheros xml que cargo al principio y guardo al final del proceso. Esto es meramente anecdótico y para evitar conexiones reales que ya se pueden sobreentender. 

 

Formulario

Descargar
 

unit URelaciones;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, DB, DBClient, TConnect, Provider;

type
  TRelaciones = class(TForm)
    lbxTipo: TListBox;
    lbxColor: TListBox;
    lbxResultados: TListBox;
    rgpCodigo: TRadioGroup;
    bnSolucion: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    cdsTipo: TClientDataSet;
    cdsTipoTipo: TStringField;
    cdsColor: TClientDataSet;
    StringField2: TStringField;
    cdsTipoIDTipo: TIntegerField;
    cdsColorIDColor: TIntegerField;
    cbxCaracteristica: TComboBox;
    cdsCaracteristica: TClientDataSet;
    cdsCaracteristicaIdCaracteristica: TIntegerField;
    cdsCaracteristicaCaracteristica: TStringField;
    chbRellenar: TCheckBox;
    Bevel1: TBevel;
    procedure FormCreate(Sender: TObject);
    procedure bnSolucionClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure chbRellenarClick(Sender: TObject);
  private
    { Private declarations }
    procedure SimulaDatosEnBBDD;
    procedure SimulaPermanenciaDatos;
  public
    { Public declarations }
  end;

var
  Relaciones: TRelaciones;

implementation

{$R *.dfm}

procedure TRelaciones.bnSolucionClick(Sender: TObject);
var
 i,j, k: Integer;
 s: String;
begin
   lbxResultados.Clear;
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do begin
          case rgpCodigo.ItemIndex of
           0: s:= lbxTipo.Items[i]+ lbxColor.Items[j];
           1: s:= lbxColor.Items[j]+ lbxTipo.Items[i];
          end;
      case chbRellenar.Checked of
        True: lbxResultados.items.Add(lbxColor.Items[j]+s);
        False: lbxResultados.items.Add(cbxCaracteristica.Items[cbxCaracteristica.ItemIndex]+s);
      end;
   end;
end;

procedure TRelaciones.chbRellenarClick(Sender: TObject);
begin
   cbxCaracteristica.Enabled:= not chbRellenar.Checked;
end;

procedure TRelaciones.FormCreate(Sender: TObject);
begin
  SimulaDatosEnBBDD;

  if not cdsColor.Active then cdsColor.Open;
  cdsColor.First;
  while not cdsColor.Eof do begin
    lbxColor.Items.Add(cdsColor.FieldByName('Color').AsString);
    cdsColor.Next;
  end;

  if not cdsTipo.Active then cdsTipo.Open;
  cdsTipo.First;
  while not cdsTipo.Eof do begin
    lbxTipo.Items.Add(cdsTipo.FieldByName('Tipo').AsString);
    cdsTipo.Next;
  end;

  if not cdsCaracteristica.Active then cdsCaracteristica.Open;
  cdsCaracteristica.First;
  while not cdsCaracteristica.Eof do begin
    cbxCaracteristica.Items.Add(cdsCaracteristica.FieldByName('Caracteristica').AsString);
    cdsCaracteristica.Next;
  end;
  cbxCaracteristica.ItemIndex:= 0;
end;

procedure TRelaciones.FormDestroy(Sender: TObject);
begin
  //guardamos los datos antes de finalizar
  SimulaPermanenciaDatos;
end;

procedure TRelaciones.SimulaDatosEnBBDD;
begin
  cdsColor.FileName:= 'Color.xml';
  cdsColor.CreateDataSet;
  cdsColor.Active:= True;
  cdsColor.Insert;
  cdsColor.FieldByName('IDColor').AsInteger:= 1;
  cdsColor.FieldByName('Color').AsString:= '10';
  cdsColor.Insert;
  cdsColor.FieldByName('IDColor').AsInteger:= 2;
  cdsColor.FieldByName('Color').AsString:= '12';
  cdsColor.Insert;
  cdsColor.FieldByName('IDColor').AsInteger:= 3;
  cdsColor.FieldByName('Color').AsString:= '15';
  cdsColor.Post;

  cdsTipo.FileName:= 'Tipo.xml';
  cdsTipo.CreateDataSet;
  cdsTipo.Active:= True;
  cdsTipo.Insert;
  cdsTipo.FieldByName('IDTipo').AsInteger:= 1;
  cdsTipo.FieldByName('Tipo').AsString:= 'AA';
  cdsTipo.Insert;
  cdsTipo.FieldByName('IDTipo').AsInteger:= 2;
  cdsTipo.FieldByName('Tipo').AsString:= 'AB';
  cdsTipo.Insert;
  cdsTipo.FieldByName('IDTipo').AsInteger:= 3;
  cdsTipo.FieldByName('Tipo').AsString:= 'CC';
  cdsTipo.Post;

  cdsCaracteristica.FileName:= 'Caracteristica.xml';
  cdsCaracteristica.CreateDataSet;
  cdsCaracteristica.Active:= True;
  cdsCaracteristica.Insert;
  cdsCaracteristica.FieldByName('IDCaracteristica').AsInteger:= 1;
  cdsCaracteristica.FieldByName('Caracteristica').AsString:= '0A';
  cdsCaracteristica.Insert;
  cdsCaracteristica.FieldByName('IDCaracteristica').AsInteger:= 2;
  cdsCaracteristica.FieldByName('Caracteristica').AsString:= '0B';
  cdsCaracteristica.Insert;
  cdsCaracteristica.FieldByName('IDCaracteristica').AsInteger:= 3;
  cdsCaracteristica.FieldByName('Caracteristica').AsString:= '0C';
  cdsCaracteristica.Post;

end;

procedure TRelaciones.SimulaPermanenciaDatos;
begin
   cdsColor.SaveToFile('color.xml');
   cdsTipo.SaveToFile('tipo.xml');
   cdsCaracteristica.SaveToFile('Caracteristica.xml');
end;

end.

Todavía no hemos hecho un solo razonamiento que nos pueda indicar que nuestro chip “pensar en clases” está activado. :-)

Eso sí, hemos sido capaces de añadir una característica más y enrevesar nuestro código otro tanto. Y dado que ahora mismo tan solo contamos con un modelo que contiene tres atributos, me hace sospechar que al tener una cantidad mayor, aumentaria la complejidad de las relaciones, (representadas en esos bucles y las distintas estructuras que permtien bifurcar el fllujo de nuestro codigo), de forma peligrosa. Esa es quizás la moraleja de esta segunda parte y la que nos debería hacer pensar que estamos atando una soga a nuestro cuello y que no va a depender de nosotros que acabe ahogandonos.

Comparar las tres versiones del bucle:

   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do
          lbxResultados.Items.Add(lbxTipo.Items[i]+ lbxColor.Items[j]);
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do
          case rgpCodigo.ItemIndex of
           0: lbxResultados.Items.Add(lbxTipo.Items[i]+ lbxColor.Items[j]);
           1: lbxResultados.Items.Add(lbxColor.Items[j]+ lbxTipo.Items[i]);
          end;
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do begin
          case rgpCodigo.ItemIndex of
           0: s:= lbxTipo.Items[i]+ lbxColor.Items[j];
           1: s:= lbxColor.Items[j]+ lbxTipo.Items[i];
          end;
      case chbRellenar.Checked of
        True: lbxResultados.items.Add(lbxColor.Items[j]+s);
        False: lbxResultados.items.Add(cbxCaracteristica.Items[cbxCaracteristica.ItemIndex]+s);
      end;
   end;

 

Podria darse el caso de que existieran relaciones de dependencia entre uno o varios atributos sin ir mas lejos. Ahora mismo no las estamos considerando. Por relaciones de dependencia entiendo por ejemplo, la existencia de restricciones que permitan combinar dos valores concretos de distintos atributos. Imaginad que para un determinado tipo no pudiera combinarse determinados colores (a efectos reales puede asimilarse a una relación maestro detalle entre los valores de dos atributos distintos, pero con la peculiaridad de que esos detalles son compartidos) .

Un ejemplo:

La bicicleta A contiene el chasis ’00′ y este puede venderse en los colores ’00′ (negro), ’01′ (rojo), ’02′ (azul)

La bicicleta B contiene el chasis ’01′ y este puede venderse en los colores  ’00′ (negro), ’03′ (beig), ’06′ (plata)

Como veis, ahora queda mas claro. Comparten el color ’00′ pero cada tipo solo se combina con determinados colores.

  ¿Que hariamos entonces? Tendriamos que actuar posiblemente en el interior de cualquiera de las estructura case antes de que se ejecutase los metodos añadir una linea a nuestros resultados de forma que se pudieran restringir las combinaciones y se pudiera desechar las que no son validas.

Es mas, la primera pregunta que podríais preguntaros es por qué tengo que suponer que sean tres, cuatro o x numero finito de elementos. Estoy simplemente generando una fotografia de los requerimientos actuales sin valorar que estos puedan cambiar y que puedan crecer el numero de ellos, por asumir que sea uno de los cambios que mas facilmente puedan producirse. Ese es uno de los errores en los que podemos facilmente caer en ocasiones. De haber contemplado esta posiblidad posiblemente hubiera sido facil generar un nuevo interfaz que asuma la nueva situación. Pensar en termino de clases no es la panacea de todos los males ni evita la complejidad intrinseca del elemento estudiado, pero si que nos permite minimizar los efectos de los cambios en los requerimientos. Un razonamiento correcto quizás nos hubiera descubierto una clase “Atributo”, capaz de manipular los distintos valores que puede manejar en funcion de un dominio. Podría establecer relaciones igualmente entre otros atributos y el mismo. Por poner algunos ejemplos que se me vienen así a “botepronto”. Asimismo, podríamos especificar una clase que permitiera gestionar añadir nuevos atributos, eliminarlos, que el orden de presentación fuera variable, etc.

Creo que todo esto encaja en la idea que mueve esta segunda parte ‘pensar en terminos de clases’… :-)

Lo dejamos aquí… y seguimos en la tercera parte. Espero y deseo que todos estos razonamientos os puedan ser de alguna utilidad.

Experimentos, reflexiones y otros artefactos (I)

mayo 13, 2009 en Advertencia, Artículos, Código, Consejo, Delphi, Entrada Diario, Taller práctico

Vamos a iniciar una pequeña serie especialmente dedicada a los programadores que se inician en Delphi y me perdonareis que no tenga la menor idea de cuantos capítulos contiene ni de cuanto tiempo se pueda extender. ¿Uno, dos? ¿quizás tres? ¿veintiuno?. Realmente os confieso que no lo se. Habitualmente se cierran las series en funcion de varias premisas: que hayas dicho todo (resulta raro), que no tengas tiempo para continuarla (supone cerrar uno o varios capitulos rápidamente e intentando que la persona que te lee no note que tienes prisa por acabar) y finalmente, que hayas iniciado un tema y no tengas ni pajolera idea de como acabarlo, bien porque has llegado a un callejon sin salida, bien porque ya no sabes como sacarle mas miga al asunto .

En realidad, mientras escribo me vienen muchas ideas a la cabeza, fruto casi siempre del día a día, pero creerme que resultan dificiles de abstraer a un contexto que sea ejemplar y reducido, como lo puede ser el contenido de un blog. La idea principal, para que me vayais comprendiendo, es quizás concienciar a este programador que se inicia en el entorno, que sufrirá durante toda su vida la tentación de olvidarse de las clases y de la orientación a objetos, para convertirse en un mero “usador” indiscriminado de ellos. Es una paradoja.  Y también una de las maldades que vienen implícitas en el sistema, como el lado oscuro de la fuerza que acompañaba al mítico Jedi. Anteriormente ya he comentado cosas como esta, que siempre he recalcado, y es precisamente por esa razón por lo que el destinatario de estas reflexiones o experimentos, es el programador que da sus primeros pasos en el entorno… 

 ¡venga!, ¡vamos!. Si. Tú… Ehhhh… Que te digo a ti…¿no tenías otras cosas que hacer…? ¡No te hagas el remolón que Tú ya sabes mas que las ratas coloradas!. ;-)   Y esta serie es solo para los programadores que se inician…

Ahora que nos hemos quedado solos (vosotros y este mendrugo que os escribe) y que nos dejan trabajar con tranquilidad, podemos hablar de las cosas sencillas.

Vamos a empezar por un ejemplo muy básico que nos permitirá ir avanzando en estas reflexiones.

Imaginaros que tenemos una producto cualquiera en nuestra manos. Podría ser una bicicleta o un despertador. ¡Qué mas da!. Lo que querais imaginar nos puede servir.

Nos vamos a valer de ese imaginario producto para hacer un ejercicio de abstracción que nos permita asignarle un tipo y un color. Y si llegamos un poco mas lejos, incluso podemos concretar que ambos tipos se definen mediante una cadena de dos caracteres que pueden ser enlazados para formar un hipotético código.

Puestos en esa tesitura, podriamos escribir unas lineas como estas:

//al pulsar el boton Solucion
procedure TmainRelaciones.btnSolucion1Click(Sender: TObject);
var
 i,j: Integer;
begin
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do
          lbxResultados.Items.Add(lbxTipo.Items[i]+ lbxColor.Items[j]);
end;

//al crearse el formulario
procedure TmainRelaciones.FormCreate(Sender: TObject);
begin
   lbxTipo.Items.Add('AA');
   lbxTipo.Items.Add('AB');
   lbxTipo.Items.Add('CC');

   lbxColor.Items.Add('10');
   lbxColor.Items.Add('12');
   lbxColor.Items.Add('15');
end;

lbxTipo, lbxColor y lbxResultados, son tres objetos de la clase TListBox que hemos dejado caer en nuestro formulario. El último, lbxResultados, nos permite concatenar una cadena de 4 caracteres y…

Sencillo. Vale. Hemos resuelto el problema y nuestro jefe estará orgulloso de nosotros porque somos la repera y le hacemos ganar dinero facilmente, PERO no hemos pensado en terminos de objetos ni de clases… Dos bucles for, anidados nos permitieron crear la estructura de un hipotetico código compuesto. Nos ha bastado encontrar un clase (contenedor) para resolver la permanencia y ¡voila!… ¿Os habeis dado cuenta como he remarcado la palabra PERO en la oración anterior?

Es una verdadera pena porque pensar en terminos de clases es bastante divertido y hasta saludable. Y no es que al razonar en  terminos de clases no pueda usarse un bucle for… ¡faltaría mas! ¡alguno habrá que todavía me lo recrimine en los post posteriores!

Como primera reflexión, esta bien ¿no?… 

Parece que no estáis demasiado conformes!. :-)  

Bueno. Vale. Extenderé el experimento y nuestro jefe, con ese aire transcendental que le caracteriza, nos comentará que en ocasiones es importante que se puedan alterar el orden de los atributos del artefacto.

Ummmmmmmmmmmmmmmmmmm….

-¡Vale! ¡Ya está!

Nuestro programador siempre encuentra soluciones y en este caso lo resolvió haciendo uso de otro componente, concretamente de la clase TRadioGroup. Éste y una estructura case, y su jefe vuelve a sonreir… :-)

procedure TmainRelaciones.btnSolucion1Click(Sender: TObject);
var
 i,j: Integer;
begin
   for i := 0 to lbxTipo.Count - 1 do
      for j := 0 to lbxColor.Count - 1 do
          case rgpCodigo.ItemIndex of
           0: lbxResultados.Items.Add(lbxTipo.Items[i]+ lbxColor.Items[j]);
           1: lbxResultados.Items.Add(lbxColor.Items[j]+ lbxTipo.Items[i]);
          end;
end;

 

Para este programador el formulario es una gran cajon donde se deposita todo y lo mas peligroso de estos razonamientos no es cómo son sus comienzos sino cuales son sus finales.

En sucesivas entradas intentaremos complicarle la vida a nuestro programador y si os parece, de forma similar, le propondremos algunos razonamientos en función de la programación orientada a objetos.