Ir al contenido


Foto

¿Record o Clase?


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

#1 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 30 junio 2012 - 02:53

Bueno, hoy me he propuesto a empezar a programar de una forma diferente a lo acostumbrado, estoy por empezar un proyecto simple para un cliente, ahora, para poder manejar los datos de usuarios, clientes, facturas, etc etc. me entró la duda, ¿Qué es mejor? ¿Records o Clase?:

Record:



delphi
  1. type
  2.   TUser = Record
  3.     UserName: String[30];
  4.     Password: String[30];
  5.     NameOfUser: String[50];
  6.     UserGroup: Integer;
  7.     GroupName: String[30];
  8.     Permisos: Array of String;
  9.     PermisosCount: Integer;
  10.   End;



Clase:



delphi
  1. type
  2.   TUser = class
  3.     UserName: String[30];
  4.     Password: String[30];
  5.     NameOfUser: String[50];
  6.     UserGroup: Integer;
  7.     GroupName: String[30];
  8.     Permisos: Array of String;
  9.     PermisosCount: Integer;
  10.    
  11.     constructor Create;
  12.     destructor Destroy; override;
  13.     procedure algo;
  14.     function algo1;
  15.   End;



Espero sus comentarios al respecto.

Saludos.
  • 0

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 30 junio 2012 - 03:50

Pues eso depende de las necesidades que tengas de usar las características de la POO para este caso.  Simplificando mucho puedes considerar una clase como un record con funciones y procedimientos propios... Si tienes que usar herencia de esas clases pues ahorras mucho código.

Yo en tu caso, si las operaciones con esos datos son varias y exclusivas a los mismos, si usaría POO. De hecho he realizado aplicaciones con tablas planas usando clases para encapsular cada elemento. Facilita mucho la tarea posterior aunque te obliga a diseñar bien las clases desde el principio.


Saludos.
  • 0

#3 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 30 junio 2012 - 03:59

Muchas gracias escafandra, usaré clases por el uso de las funciones y procedimientos.

... Facilita mucho la tarea posterior aunque te obliga a diseñar bien las clases desde el principio.


Antes de empezar ya me lo estaba imaginando :). Asì que espero hacerlo bien desde el principio :D.

Saludos.
  • 0

#4 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 30 junio 2012 - 05:08

El paradigma OO vino para quedarse  ;) El pensamiento estructurado dio un paso más y giró sus conceptos para traerle más vida y hacerles el trabajo de forma más fácil y hasta me animaría a decir... humana y realista a los desarrolladores. Y eso fue en la década del 70 cuando empezó a surgir los conceptos del paradigma OO, desde los años 90 que la POO reina, y posiblemente lo reine por siempre.

Si empleas records para mantener la información eso te llevará a trabajar con mecanismos de control y paso de parámetros de estos tipos... a trabajar con la vieja escuela. Esta forma de trabajar lleva a que al menos entre el 25% y el 50% de tus módulos lleven a un Acoplamiento Estampado, de Control o hasta incluso llegar al Acoplamiento por Contenido o Patológico afectando enormemente a la cohesión en general.

Sólo en un proyecto chico y medio básico o elemental propondría "bajar de nivel" y moverme por el pensamiento puramente estructurado.

Ahora bien, puedes hacer algo salomónico... como a lo que yo hago. Mis módulos o unidades bases están orientadas a la vieja escuela y ofrecen una estructura básica que da soporte (tanto los tipos como las operaciones) a las unidades o módulos superiores que están enfocados al pensamiento POO.
Un principal motivo de porqué lo hago de esta manera: claridad entre cohesión y acoplamiento y una manera de separar lo que es una estructura pura y estática de lo que es una clase que por lo general tiene más flexibilidad y está asociada con una característica de "viva".
Por ejemplo, pensemos en una matriz. ¿Que sería lo más adecuado para implementar operaciones sobre estas estructura de datos?

Podría realizar una clase TMatrix que contenga métodos como Traspose(), Trace(), Multiply(), IsIdentity() y otros más de interés. Y dentro de ésta mantener en memoria la propia matriz de toda la vida:



delphi
  1. type
  2.   TEMatrix = array of TVector;



Al principio yo estuve pensando que podría ser muy interesante llevarlo así pero luego sentí que estaba mezclando demasiado el pensamiento estructurado con el OO y no lo veía como algo bueno. Otra cosa que me evitó seguir este enfoque es que luego veería en mi código cosas como:



delphi
  1. MA.Multiply(MV);



Y me llevaría escenarios confusos... ¿Donde meto el resultado de esa operación? ¿En la clase que invoca el método, en la que paso como parámetro? ¿O no será mejor esto?



delphi
  1. MA.Mutiply(MV, Res);



Y que sea Res el objeto que mantenga el resultado de la operación.
Esto no es bueno, porque incluso me llevaría a una mezcla de cohesión y acoplamiento entre otras estructuras, como un TVector. Hay operaciones que se realizan en conjunto y en doble sentido, entre vectores y matrices....
Implementar estos métodos y basarlo en clases me llevaría a que de algún modo internamente cada clase debiera conocer algunos detalles internos, y de bajo nivel, de la otra para que se pueda realizar las operaciones.

Todo eso me dijo que no era nada saludable. Conclusión: dejar a la estructuras de datos primitivas tal como son y no pensarlas como si fuera una clase. Luego las clases se limitan a abstraer las cosas desde otra perspectivas y controlar el estado general de los datos y dejan el trabajo "sucio" a quien lo hace mejor. No fue fácil descubrir el nuevo contexto de las clases, pero a la larga ahora me resultó mejor.

Saludos,
  • 0

#5 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 01 julio 2012 - 09:31

Yo uso ambos, y suelo hacer lo siguiente:

[Delphi]
type

Rdireccion = Record
  calle: string;
  numero: string;
  colonia: string;
  estado: string
  pais: string;
end;

Tusuarios = class
  FID: integer;
  FNombre: string;
  FPaterno: string;
  FMaterno: string;
  FDireccion: Rdireccion;

  private

  public

  published

end;
[/Delphi]

Saludos
  • 0

#6 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 01 julio 2012 - 11:55

Pues si, es una posibilidad amigo Eliseo. Es legal esa forma, y hasta en ocasiones bastante simple y directa.

El defecto que tiene es que ahora, si existe otra clase que se relacione con ésta y haga uso de dicho registro para hacer su trabajo no sólo se necesita hacer un acoplamiento entre las clases (lo habitual, y algo no muy problemático, el Acoplamiento de Datos; el 1er tipo de Acoplamiento aconsejable) sino que además se genera un acoplamiento del tipo Estampado (que si bien es el 2do tipo* de acoplamiento más habitual, ya se está entrando en acoplamientos menos aconsejables). O si es que ambas clases deben acceder y comparten esta estructura en común estás generando un acoplamiento Común. Tanto el Estampado como el Común pueden derivar en un Acoplamiento Patológico sino se controla la situación.

* En realidad existe un Acoplamiento de orden 0, que es llamado Acoplamiento sin Cuplas. Este acomplamiento es aquel que no requiere de datos, estructura o bandera de control o híbrida... sino una simple invocación que delega el trabajo, y no regresa un resultado. Es decir algo como:



delphi
  1. procedure TClaseA.MandarB;
  2. begin
  3.   FClaseB.HacerAlgo;
  4. end;



Se puede ver que el método HacerAlgo de la clase TClaseB (FClaseB es un atributo) no requiere dato alguno ni devuelve algo que necesite TClaseA.

Volviendo a tu ejemplo amigo, si la clase TUsuario necesita trabajar con por ejemplo TOrdenCompra y por alguna cuestión TOrdenCompra requiere de esta información polémica hay dos opciones:
1) Que TOrdenCompra y TUsuario trabajen con la estructura, y que TUsuarios disponga de un método que devuelta ésta:



delphi
  1. function TUsuario.GetDireccion: RDireccion;



Generando el Estampado.

2) Evitar el estampado y convertirlo en el de Datos haciendo que TUsuario regrese la información a partes y de interés:



delphi
  1. function TUsuario.GetDireccionCalle: string;
  2. function TUsuario.GetNumero: string;
  3. function ...



El defecto de esta técnica es que ahora la clase TUsuario ha perdido cohesión, y recomponer la información completa requiere de varios mensajes.

Recuerden que el primer principio que dio la POO es el Principio Abierto/Cerrado y se supone que una clase debe cuidar de no mostrar más de lo necesario. Podríamos preguntarnos si es que esta información sólo será de uso interno para la clase o si es que otras deben conocerlo.

Si resulta ser que esta información empaquetada en el registro es de demasiado interés y estas dos (o incluso más) clases van a trabajar con estas, quizá sea que hay otra entidad no descubierta... justamente esta estructura. Es decir, no como un tipo estructurado sino como una clase más. Entonces, esta información queda oculta en sus atributos y ofrecer los métodos de lectura y/o escritura con la debida protección que requieran. Ahora estas dos clases se convierten en clientes de esta nueva TDireccion. Por ejemplo:



delphi
  1. TDireccion = class
  2. private
  3.   FCalle: string;
  4.   ...
  5.   function GetCalle: string;
  6.   procedure SetCalle(const ACalle: string);
  7.   ....
  8. public
  9.   property Calle: string read GetCalle write SetCalle;
  10.   ...



Resulta ser que muchas veces lo que tenemos como información empaquetada como un record al tiempo le encontramos ciertas operaciones que necesitamos y se convierte en una clase más del contexto.

Mi consejo: revisar el contexto y ver que sugiere.  ;)

Saludos,
  • 0

#7 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 01 julio 2012 - 12:38

Vaya que me ha gustado la dirección del tema :), fijense, en mi clase TUsuario tengo la función Login, lo hice de esta manera:



delphi
  1. function Login(User,Pass: String; DataBase: TIBDatabase; var NameUser,Nombre,Grupo: string): boolean;



Esa función verifica la existencia de un usuario antes del logeo, si existe, devuelve toda la información del usuario logeado, hasta ahora me ha funcionado perfecto, pero antes de seguir avanzando en el proyecto, ¿Tiene alguna desventaja o ventaja hacerlo de esa manera?, ¿Donde es mejor colocar esa función dentro de la clase: Private, public, published (Ahora está fuera de todo eso. Ver código más abajo)?.



delphi
  1. type
  2.   TUser = class
  3.     UserName: String[30];
  4.     Password: String[30];
  5.     NameOfUser: String[50];
  6.     UserGroup: Integer;
  7.     GroupName: String[30];
  8.     Permisos: Array of String;
  9.     PermisosCount: Integer;
  10.  
  11.     constructor Create;
  12.     function Login(User,Pass: String; DataBase: TIBDatabase; var NameUser,Nombre,Grupo: string): boolean;
  13.   End;



Saludos.
  • 0

#8 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 01 julio 2012 - 12:43

Pues si, es una posibilidad amigo Eliseo. Es legal esa forma, y hasta en ocasiones bastante simple y directa.

El defecto que tiene es que ahora, si existe otra clase que se relacione con ésta y haga uso de dicho registro para hacer su trabajo no sólo se necesita hacer un acoplamiento entre las clases (lo habitual, y algo no muy problemático, el Acoplamiento de Datos; el 1er tipo de Acoplamiento aconsejable) sino que además se genera un acoplamiento del tipo Estampado (que si bien es el 2do tipo* de acoplamiento más habitual, ya se está entrando en acoplamientos menos aconsejables). O si es que ambas clases deben acceder y comparten esta estructura en común estás generando un acoplamiento Común. Tanto el Estampado como el Común pueden derivar en un Acoplamiento Patológico sino se controla la situación.

* En realidad existe un Acoplamiento de orden 0, que es llamado Acoplamiento sin Cuplas. Este acomplamiento es aquel que no requiere de datos, estructura o bandera de control o híbrida... sino una simple invocación que delega el trabajo, y no regresa un resultado. Es decir algo como:



delphi
  1. procedure TClaseA.MandarB;
  2. begin
  3.   FClaseB.HacerAlgo;
  4. end;



Se puede ver que el método HacerAlgo de la clase TClaseB (FClaseB es un atributo) no requiere dato alguno ni devuelve algo que necesite TClaseA.

Volviendo a tu ejemplo amigo, si la clase TUsuario necesita trabajar con por ejemplo TOrdenCompra y por alguna cuestión TOrdenCompra requiere de esta información polémica hay dos opciones:
1) Que TOrdenCompra y TUsuario trabajen con la estructura, y que TUsuarios disponga de un método que devuelta ésta:



delphi
  1. function TUsuario.GetDireccion: RDireccion;



Generando el Estampado.

2) Evitar el estampado y convertirlo en el de Datos haciendo que TUsuario regrese la información a partes y de interés:



delphi
  1. function TUsuario.GetDireccionCalle: string;
  2. function TUsuario.GetNumero: string;
  3. function ...



El defecto de esta técnica es que ahora la clase TUsuario ha perdido cohesión, y recomponer la información completa requiere de varios mensajes.

Recuerden que el primer principio que dio la POO es el Principio Abierto/Cerrado y se supone que una clase debe cuidar de no mostrar más de lo necesario. Podríamos preguntarnos si es que esta información sólo será de uso interno para la clase o si es que otras deben conocerlo.

Si resulta ser que esta información empaquetada en el registro es de demasiado interés y estas dos (o incluso más) clases van a trabajar con estas, quizá sea que hay otra entidad no descubierta... justamente esta estructura. Es decir, no como un tipo estructurado sino como una clase más. Entonces, esta información queda oculta en sus atributos y ofrecer los métodos de lectura y/o escritura con la debida protección que requieran. Ahora estas dos clases se convierten en clientes de esta nueva TDireccion. Por ejemplo:



delphi
  1. TDireccion = class
  2. private
  3.   FCalle: string;
  4.   ...
  5.   function GetCalle: string;
  6.   procedure SetCalle(const ACalle: string);
  7.   ....
  8. public
  9.   property Calle: string read GetCalle write SetCalle;
  10.   ...



Resulta ser que muchas veces lo que tenemos como información empaquetada como un record al tiempo le encontramos ciertas operaciones que necesitamos y se convierte en una clase más del contexto.

Mi consejo: revisar el contexto y ver que sugiere.  ;)

Saludos,


Claro, claro, sólo puse un ejemplo y fué lo que me vino a la mente :).

Por supuesto que para cuestiones como las que sugieres, utilizo clases dentro de otras clases sobre todo cuando dicha información es compartida por más de una clase, pero cuando es única, me basta con un record,. no requiero de más.

De cualquier forma lo que has expuesto está excelente y enriquece éste hilo. (y)

Saludos

Saludos
  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 01 julio 2012 - 01:32

Hola Fernando, viendo ese diseño de la clase y el método en cuestión yo diría:
1) ¿Es necesario y obligatorio restringir los strings a esos tamaño? Me recuerda a cuando definía records en Pascal y debía guardar en archivos de registros  :D

2) Si los parámetros de entrada para Login() se extraen de los atributos no hace falta pasarlos ya que puede recuperarse y leerlos de forma directa. Lo mismo aplica para los de salida, Te puedes ahorrar 5 parámetros.  ;) Es decir:



delphi
  1. function Login(DataBase: TIBDatabase): boolean;



Y en su implementación simplemente haces la lectura/escritura de los atributos en donde necesites.

3) Sugiero que nombres a los atributos con la nomeclatura F<nombre-atributo>.

Respecto a la visibilidad de la función habría que ver desde donde, y cuando, se pretende invocarla. Como puede ser algo para hacer por si mismo (que puede ir desde Private hasta Public/Published), como puede venir desde una orden desde otra clase (que obviamente requiere de Public).

Saludos,
  • 0

#10 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 01 julio 2012 - 01:42

Claro, claro, sólo puse un ejemplo y fué lo que me vino a la mente :).

Por supuesto que para cuestiones como las que sugieres, utilizo clases dentro de otras clases sobre todo cuando dicha información es compartida por más de una clase, pero cuando es única, me basta con un record,. no requiero de más.

De cualquier forma lo que has expuesto está excelente y enriquece éste hilo. (y)

Saludos

Saludos

Asi pues si. :D
Yo ya me acostumbré a pensar en POO casi en forma exclusiva. A veces resulta que uno mete tantos atributos y métodos para una clase (algo que solía... bueno este... ,suele, pasarme muy a menudo) que resulta mejor separar y diseñar dos clases.

Esto que comenté y lo de generar una clase a a partir de información estructurada o dispuesta en otra es un patrón más de los cientos; aunque no tiene un nombre específico. Está motivado por el patrón Alta Cohesión y Bajo Acoplamiento. Un caso particular de él es el "patrón" "Descripción" (no tiene un nombre definido y hay quienes no lo aceptan como un patrón), que burdamente comenté en un hilo sobre una discusión por el tema de control de stock al hablar sobre artículos vs descripción del artículo.

Justamente amigo mi intención era enriquecer el hilo, y de paso ver si rompo otros cocos... No es justo que sólo el mío se rompa con berenjales :D

Saludos,
  • 0

#11 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 02 julio 2012 - 02:12

Buenas

Un par de apreciaciones viendo el código

  • Es buena costumbre hacer que el constructor sea virtual, más aun si tienes pensado que sea la clase base de otras (aunque en el ejemplo quizás no sea necesario)
  • Aunque implícitamente te esté cogiendo la visibilidad de public, es buena costumbre ponerlo explícitamente, el código queda mucho más claro
  • Si sólo usarás esa clase para loguear el usuario, quizás te interese hacer la función Login como una función de clase para ahorrar trabajo al programador (que serás tú) en la invocación
  • De la visibilidad published puedes olvidarte. Personalmente sólo le veo utilidad en la creación de componentes (es lo que aparece en el inspector de objetos y con lo que se genera el dfm)
  • Como comenta Delphius, usa la nomenclatura Delphi y antepón una F a las propiedades
  • Usa properties en lugar de variables. Con ellas podrás programar métodos Get y Set además de poder diferenciar entre la variable "externa a la clase" (la que iría sin la F) y la interna de la clase (la que iría con la F)

Dejaría algo así tu clase:



delphi
  1.   TUser = class(TObject)
  2.   private
  3.     FPermisosCount: Integer;
  4.     FNameOfUser: string;
  5.     FPassword: string;
  6.     FUserGroup: Integer;
  7.     FPermisos: array of string;
  8.     FUserName: string;
  9.     FGroupName: string;
  10.   public
  11.     constructor Create; virtual;
  12.  
  13.     function Login(DataBase: TIBDatabase): Boolean;
  14.  
  15.     property UserName: string read FUserName write FUserName;
  16.     property Password: string read FPassword write FPassword;
  17.     property NameOfUser: string read FNameOfUser write FNameOfUser;
  18.     property UserGroup: Integer read FUserGroup write FUserGroup;
  19.     property GroupName: string read FGroupName write FGroupName;
  20.     property Permisos: Array of string read FPermisos write FPermisos;
  21.     property PermisosCount: Integer read FPermisosCount write FPermisosCount;
  22.   end;



De esta manera, desde fuera de la clase se verá, por ejemplo, "UserName" y no "FUserName". Además, el uso de "UserName" disparará el posible Get/Set que estuviera asociado. En cambio, dentro de la clase, el uso de FUserName no dispara el posible Get/Set asociado pudiendo cambiar el valor del mismo tranquilamente

Nos leemos

  • 0

#12 Rolphy Reyes

Rolphy Reyes

    Advanced Member

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

Escrito 02 julio 2012 - 10:48

Saludos.

Cabe resaltar que a partir de Delphi 2005, los records tiene un comportamiento similar a las clases.

Lectura.
  • 0




IP.Board spam blocked by CleanTalk.