Experimentos, reflexiones y otros artefactos (II)

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

¿Por donde ibamos? Sí. Ya…

experimento1

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.

 

relaciones

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.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Blog de WordPress.com.

Subir ↑

Recetas y consejos nutricionales

Indicadas para personas con diabetes, recomendadas para todos.

¡Buen camino!

ANÉCDOTAS Y REFLEXIONES SOBRE UN VIAJE A SANTIAGO…

http://lfgonzalez.visiblogs.com/

Algunas reflexiones y comentarios sobre Delphi

It's All About Code!

A blog about Delphi and related technologies

The Podcast at Delphi.org

The Podcast about the Delphi programming language, tools, news and community.

Blog de Carlos G

Algunas reflexiones y comentarios sobre Delphi

The Road to Delphi

Delphi - Free Pascal - Oxygene

La web de Seoane

Algunas reflexiones y comentarios sobre Delphi

El blog de cadetill

Cosas de programación....... y de la vida

Delphi-losophy

A Lover of Delphi Wisdom

Delphi en Movimiento

Algunas reflexiones y comentarios sobre Delphi

marcocantu.blog

Algunas reflexiones y comentarios sobre Delphi

/*Prog*/ Delphi-Neftalí /*finProg*/

Blog sobre programación de Neftalí -Germán Estévez-

Press F9

Algunas reflexiones y comentarios sobre Delphi

El blog de jachguate

Un blog sobre tecnología y la vida en general

A %d blogueros les gusta esto: