39 - POO - propiedades

Otro concepto que incorpora Delphi son las propiedades. Una propiedad es un identificador con un determinado tipo de dato que accede normalmente a un campo en forma directa o a través de un método.

Lo más adecuado si queremos implementar correctamente la programación orientada a objetos es que los campos sean privados y si necesitamos acceder a ellos se haga mediante propiedades.

Una propiedad nos permite agregar un método que valide si el dato que queremos asignar a la propiedad se puede almacenar en el campo.

Problema 1

Confeccionar una clase que permita cargar el nombre y la edad de una persona. Mostrar los datos cargados. Imprimir un mensaje si es mayor de edad (edad>=18) o no.
Definir una propiedad para acceder al campo que almacena la edad de la persona.

Proyecto141

program Proyecto141;

{$APPTYPE CONSOLE}

type
  TPersona = class
  private
    Nombre: String;
    FEdad: Integer;
    procedure FijarEdad(ed: Integer);
  public
    constructor create(nom: String; ed: Integer);
    procedure Imprimir;
    procedure EsMayorDeEdad;
    property Edad: Integer read FEdad write FijarEdad;
  end;

constructor TPersona.create(nom: string; ed: Integer);
begin
  Nombre := Nom;
  Edad := Ed;
end;

procedure TPersona.Imprimir;
begin
  WriteLn('Nombre:', Nombre, ' Edad:', Edad);
end;

procedure TPersona.EsMayorDeEdad;
begin
  if Edad >= 18 then
    WriteLn('Es mayor de edad')
  else
    WriteLn('No es mayor de edad');
end;

procedure TPersona.FijarEdad(ed: Integer);
begin
  if ed >= 0 then
    FEdad := ed
  else
    FEdad := 0
end;

var
  Persona1: TPersona;
begin
  Persona1 := TPersona.create('jose', 23);
  Persona1.Imprimir;
  Persona1.EsMayorDeEdad;
  Persona1.Edad := 24;
  Persona1.Imprimir;
  Persona1.Free;
  ReadLn;
end.

En principio aumenta mucho el código cuando asociamos a un campo una propiedad. El campo que almacena la edad de la persona lo llamamos FEdad:

    FEdad: Integer;

Es común empezar con el caracter F (Field = campo) un campo si luego se asocia a una propiedad.

La sintaxis para definir una propiedad es:

    property Edad: Integer read FEdad write FijarEdad;

La propiedad Edad es de tipo Integer y cuando consultemos su valor lo lee del campo (FEdad) y cuando le asignemos un valor a la propiedad luego se ejecuta el método FijarEdad.

El método FijarEdad es un método que debe recibir un parámetro del mismo tipo de dato de la propiedad:

procedure TPersona.FijarEdad(ed: Integer);
begin
  if ed >= 0 then
    FEdad := ed
  else
    FEdad := 0
end;

Todas estas vueltas tiene como objetivo poder controlar los datos que se almacenan en los campos.

Cuando en el bloque principal del programa hacemos la asignación de un entero a la propiedad en realidad se está ejecutando el método FijarEdad:

  Persona1.Edad := 24;

Recordemos que desde fuera de la clase podemos acceder a la propiedad y no al campo FEdad.

Lo mismo sucede cuando queremos recuperar el valor de una propiedad:

  WriteLn(Persona1.Edad);

En este caso se ejecuta la cláusula read de la propiedad que en nuestra caso es "read FEdad" (recuperar el valor almacenado en el campo FEdad)

Dentro de la misma clase accedemos a la propiedad:

constructor TPersona.create(nom: string; ed: Integer);
begin
  Nombre := Nom;
  Edad := Ed;
end;

En principio si bien podemos codificar:

constructor TPersona.create(nom: string; ed: Integer);
begin
  Nombre := Nom;
  FEdad := Ed;
end;

Luego perdemos las ventajas de poder validar cuando llega una edad con valor negativo:

  Persona1 := TPersona.create('jose', -5);

Con la segunda sintáxis del constructor se almacena un -5 en el campo FEdad, en cambio con la primer sintaxis accediendo a la propiedad se almacena un cero (una edad nunca puede ser negativa)

Problema 2

Declarar una clase TDado que permite tirarlo y generar un valor entre 1 y 6. Definir una propiedad que permita acceder al valor almacenado en el dado pero que solo se permita cambiar solo tirándolo de nuevo.

Proyecto142

program Proyecto142;

{$APPTYPE CONSOLE}

type
  TDado = class
    private
      FLado: Integer;
    public
      procedure Tirar;
      property Lado: Integer read FLado;
  end;

procedure TDado.Tirar;
begin
  FLado := Random(6) + 1;
end;

var
  Dado1: TDado;
begin
  Randomize;
  Dado1 := TDado.Create;
  Dado1.Tirar;
  WriteLn('Valor del dado:', Dado1.Lado);
  Dado1.Tirar;
  WriteLn('Valor del dado luego de tirar nuevamente:', Dado1.Lado);
  ReadLn;
end.

Podemos definir que una propiedad sea de solo lectura simplemente no definiendo la sección write:

      property Lado: Integer read FLado;

Para tirar el dado modificamos el campo FLado:

procedure TDado.Tirar;
begin
  FLado := Random(6) + 1;
end;

Para consultar el valor del dado accedemos a la propiedad Lado:

  WriteLn('Valor del dado:', Dado1.Lado);

Como la propiedad es de solo lectura se produce un error de sintaxis si intentamos modificar el valor del Lado:

  Dado1.Lado := 7;