34 - Declaración de una clase y definición de objetos

La programación orientada a objetos se basa en la declaración de clases; a diferencia de la programación estructurada, que está centrada en la definición de procedimientos y funciones.

Una clase es un molde del que luego se pueden crear múltiples objetos, con similares características.

Una clase es una plantilla (molde), que define campos o atributos (variables) y métodos (procedimientos y funciones)

La clase define los atributos y métodos comunes a los objetos de ese tipo, pero luego, cada objeto tendrá sus propios valores y compartirán las mismas funciones.

Debemos declarar una clase antes de poder crear objetos (instancias) de esa clase. Al crear un objeto de una clase, se dice que se crea una instancia de la clase o un objeto propiamente dicho.

La declaración de una clase, su implementación y la definición de un objeto la veremos mediante un ejemplo.

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

Proyecto124

program Proyecto124;

{$APPTYPE CONSOLE}

type
  TPersona = class
    Nombre: String;
    Edad: Integer;
    procedure Cargar;
    procedure Imprimir;
    procedure EsMayorDeEdad;
  end;

procedure TPersona.Cargar;
begin
  Write('Ingrese el nombre de la persona:');
  ReadLn(Nombre);
  Write('Ingrese la edad:');
  ReadLn(Edad);
end;

procedure TPersona.Imprimir;
begin
  WriteLn('Datos de la persona');
  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;


var
  Persona1: TPersona;
begin
  Persona1 := TPersona.Create;
  Persona1.Cargar;
  Persona1.Imprimir;
  Persona1.EsMayorDeEdad;
  Persona1.Free;
  ReadLn;
end.

La declaración de una clase es similar a la declaración de un registro, debemos definir los campos de la clase y dentro de la misma declarar los métodos (procedimientos y funciones) que acceden a los campos:

  TPersona = class
    Nombre: String;
    Edad: Integer;
    procedure Cargar;
    procedure Imprimir;
    procedure EsMayorDeEdad;
  end;

La clase TPersona tiene dos campos llamados Nombre y Edad, y tres métodos llamados Cargar, Imprimir y EsMayorDeEdad.

Como vemos solo dentro de la estructura class declaramos los métodos, luego debemos implementarlos.

Para implementar los métodos debemos anteceder al nombre del método a que clase pertenece dicho método:

procedure TPersona.Cargar;
begin
  Write('Ingrese el nombre de la persona:');
  ReadLn(Nombre);
  Write('Ingrese la edad:');
  ReadLn(Edad);
end;

Los métodos tienen por objetivo cargar, consultar, modificar, imprimir etc. los campos.

En el método Imprimir se muestran los contenidos de los campos Nombre y Edad que fueron cargados en el método Cargar:

procedure TPersona.Imprimir;
begin
  WriteLn('Datos de la persona');
  WriteLn('Nombre:', Nombre,' Edad:', Edad);
end;

Por último el método EsMayorDeEdad verifica el contenido del campo Edad y dependiendo de su contenido procedemos a mostrar si es mayor de edad o no:

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

Acotaciones: Un if en Delphi puede no llevar los bloques begin y end si en su interior hay una sola instrucción. Anteriormente no utilizamos esta característica para evitar errores (lo mismo se aplica para las estructuras repetitivas while y for).

El siguiente paso es definir un objeto de la clase TPersona en la zona de la var:

var
  Persona1: TPersona;

Para poder llamar a los métodos del objeto Persona1 debemos crear el objeto con la siguiente sintaxis:

  Persona1 := TPersona.Create;

El método Create no lo hemos creado nosotros en la clase TPersona (veremos más adelante el concepto de herencia y como la clase TPersona hereda el método Create)

El método Create es el primero que debemos llamar y le antecedemos el nombre de la clase. Este método reserva espacio en la memoria para el almacenamiento del objeto Persona1 (si no lo llamamos luego se genera un error en tiempo de ejecución)

Una vez creado el objeto Persona1 podemos llamar a sus métodos antecediendo el nombre del objeto:

  Persona1.Cargar;
  Persona1.Imprimir;
  Persona1.EsMayorDeEdad;

La ventaja de la programación orientada a objetos es poder encapsular campos y métodos dentro de una clase. Podemos luego declarar otra clase que tenga el mismo nombre de método "Imprimir" pero no habrá problemas porque se encuentra encapsulado dentro de una clase.

La POO (Programación Orientada a Objetos) presenta grandes beneficios cuando un programa es muy grande (decenas o cientos de miles de líneas)

La POO permite agrupar distintas funcionalidades y datos en distintas clases y facilitar su uso.

Otro punto fundamental en Delphi para trabajar con objetos es liberar el espacio de memoria cuando no necesitamos trabajar más con él llamando al método Free:

  Persona1.Free;

El método Create es el primero que llamamos y el método Free es el último(al método Create lo llamamos antecediendo el nombre de la clase pero al método Free lo llamamos antecediendo el nombre del objeto)

Veremos luego que el método Free es heredado cuando declaramos una clase en Delphi.

Problema 2

Modificar el problema anterios para realizar la carga del nombre y la edad de dos personas. Mostrar los datos cargados. Imprimir un mensaje si son mayor de edad o no.

Proyecto125

program Proyecto125;

{$APPTYPE CONSOLE}

type
  TPersona = class
    Nombre: String;
    Edad: Integer;
    procedure Cargar;
    procedure Imprimir;
    procedure EsMayorDeEdad;
  end;

procedure TPersona.Cargar;
begin
  Write('Ingrese el nombre de la persona:');
  ReadLn(Nombre);
  Write('Ingrese la edad:');
  ReadLn(Edad);
end;

procedure TPersona.Imprimir;
begin
  WriteLn('Datos de la persona');
  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;


var
  Persona1: TPersona;
  Persona2: TPersona;
begin
  Persona1 := TPersona.Create;
  WriteLn('Datos de la primer persona');
  Persona1.Cargar;
  Persona2 := TPersona.Create;
  WriteLn('Datos de la segunda persona');
  Persona2.Cargar;
  Persona1.Imprimir;
  Persona1.EsMayorDeEdad;
  Persona1.Free;
  Persona2.Imprimir;
  Persona2.EsMayorDeEdad;
  Persona2.Free;
  ReadLn;
end.

Lo más importante es notar que básicamente lo único que cambia es la definición de dos objetos de la clase TPersona:

var
  Persona1: TPersona;
  Persona2: TPersona;

La clase no se ha modificado, solo la definición de dos objetos y las llamadas a los métodos respectivos de cada objeto (recordemos que una clase es un molde del cual podemos luego definir tantos objetos como sean necesarios)

Más adelante veremos que si codificamos un programa que sea una calculadora tendremos la clase "TBoton" y luego definiremos 19 objetos de la clase "TBoton".

Problema 3

Desarrollar un programa que cargue los lados de un triángulo e implemente los siguientes métodos: inicializar los campos, imprimir el valor del lado mayor y otro método que muestre si es equilátero o no.

Proyecto126

program Proyecto126;

{$APPTYPE CONSOLE}

type
  TTriangulo = class
    Lado1: Integer;
    Lado2: Integer;
    Lado3: Integer;
    procedure Cargar;
    procedure LadoMayor;
    procedure EsEquilatero;
  end;

procedure TTriangulo.Cargar;
begin
  Write('Ingrese primer lado:');
  ReadLn(Lado1);
  Write('Ingrese segundo lado:');
  ReadLn(Lado2);
  Write('Ingrese tercer lado:');
  ReadLn(Lado3);
end;

procedure TTriangulo.LadoMayor;
begin
  Write('Lado mayor:');
  if (Lado1 > Lado2) And (Lado1 > Lado2) then
    WriteLn(Lado1)
  else
    if Lado1 > Lado2 then
      WriteLn(Lado2)
    else
      WriteLn(Lado3);
end;

procedure TTriangulo.EsEquilatero;
begin
  if (Lado1 = Lado2) and (Lado1 = Lado3) then
    WriteLn('Es un triangulo equilatero')
  else
    WriteLn('No es un triangulo equilatero');
end;


var
  Triangulo1: TTriangulo;
begin
  Triangulo1 := TTriangulo.Create;
  Triangulo1.Cargar;
  Triangulo1.LadoMayor;
  Triangulo1.EsEquilatero;
  Triangulo1.Free;
  ReadLn;
end.

El nombre de la clase es TTriangulo (En Delphi la propuesta es que todos los tipos de datos empiecen con el caracter T) y requiere definir tres campos de tipo Integer donde almacenamos los valores de los lados del triángulo.
También declaramos la cabecera de los tres métodos requeridos para resolver el problema.:

type
  TTriangulo = class
    Lado1: Integer;
    Lado2: Integer;
    Lado3: Integer;
    procedure Cargar;
    procedure LadoMayor;
    procedure EsEquilatero;
  end;

Para implementar cada método debemos anteceder el nombre de la clase, dentro del método podemos acceder a todos los campos definidos en la clase directamente por su nombre:

procedure TTriangulo.Cargar;
begin
  Write('Ingrese primer lado:');
  ReadLn(Lado1);
  Write('Ingrese segundo lado:');
  ReadLn(Lado2);
  Write('Ingrese tercer lado:');
  ReadLn(Lado3);
end;

El método LadoMayor muestra el valor mayor de los tres enteros ingresados por teclado en el método Cargar, es importante tener en cuenta que desde el bloque principal del programa primero debemos llamar a Cargar y luego a LadoMayor:

procedure TTriangulo.LadoMayor;
begin
  Write('Lado mayor:');
  if (Lado1 > Lado2) And (Lado1 > Lado2) then
    WriteLn(Lado1)
  else
    if Lado1 > Lado2 then
      WriteLn(Lado2)
    else
      WriteLn(Lado3);
end;

El último método de esta clase verifica si los tres campos tienen almacenado un valor igual:

procedure TTriangulo.EsEquilatero;
begin
  if (Lado1 = Lado2) and (Lado1 = Lado3) then
    WriteLn('Es un triangulo equilatero')
  else
    WriteLn('No es un triangulo equilatero');
end;

Previo al bloque principal begin end. definimos un objeto de la clase TTriangulo:

var
  Triangulo1: TTriangulo;

En el bloque principal creamos el objeto llamando al método Create antecediendo el nombre de la clase:

begin
  Triangulo1 := TTriangulo.Create;
Seguidamente llamamos los métodos creados por nosotros en la clase TTriangulo:

  Triangulo1.Cargar;
  Triangulo1.LadoMayor;
  Triangulo1.EsEquilatero;

Y finalmente ejecutamos el método Free para liberar espacio de memoria del objeto:

  Triangulo1.Free;
  ReadLn;
end.

Problema 4

Desarrollar una clase que represente un punto en el plano y tenga los siguientes métodos: cargar los valores de x e y, imprimir en que cuadrante se encuentra dicho punto (concepto matemático, primer cuadrante si x e y son positivas, si x<0 e y>0 segundo cuadrante, etc.)

Proyecto127

program Proyecto127;

{$APPTYPE CONSOLE}

type
  TPunto = class
    X: Integer;
    Y: Integer;
    procedure Cargar;
    procedure ImprimirCuadrante;
  end;

procedure TPunto.Cargar;
begin
  Write('Ingrese coordenada x :');
  ReadLn(X);
  Write('Ingrese coordenada y :');
  ReadLn(Y);
end;

procedure TPunto.ImprimirCuadrante;
begin
  if (X > 0) and (y > 0) then
    WriteLn('Se encuentra en el primer cuadrante.')
  else
    if (X < 0) and (Y > 0) then
      WriteLn('Se encuentra en el segundo cuadrante.')
    else
      if (X < 0) and (Y < 0) then
        WriteLn('Se encuentra en el tercer cuadrante.')
      else
        if (X > 0) and (Y < 0) then
          WriteLn('Se encuentra en el cuarto cuadrante.')
        else
          WriteLn('El punto no está en un cuadrante.');
end;


var
  Punto1: TPunto;
begin
  Punto1 := TPunto.Create;
  Punto1.Cargar;
  Punto1.ImprimirCuadrante;
  Punto1.Free;
  ReadLn;
end.

A la clase la llamamos TPunto y en la declaración definimos dos campos y dos métodos:

  TPunto = class
    X: Integer;
    Y: Integer;
    procedure Cargar;
    procedure ImprimirCuadrante;
  end;

El método Cargar pide ingresar por teclado las coordenadas x e y:

procedure TPunto.Cargar;
begin
  Write('Ingrese coordenada x :');
  ReadLn(X);
  Write('Ingrese coordenada y :');
  ReadLn(Y);
end;

El segundo método mediante un conjunto de if anidados verificamos en que cuadrante se encuentra el punto ingresado:

procedure TPunto.ImprimirCuadrante;
begin
  if (X > 0) and (y > 0) then
    WriteLn('Se encuentra en el primer cuadrante.')
  else
    if (X < 0) and (Y > 0) then
      WriteLn('Se encuentra en el segundo cuadrante.')
    else
      if (X < 0) and (Y < 0) then
        WriteLn('Se encuentra en el tercer cuadrante.')
      else
        if (X > 0) and (Y < 0) then
          WriteLn('Se encuentra en el cuarto cuadrante.')
        else
          WriteLn('El punto no está en un cuadrante.');
end;

El bloque principal no tiene grandes diferencias con los problemas realizados anteriormente, definimos un objeto de la clase TPunto, creamos el objeto, llammamos a sus métodos y liberamos espacio del objeto:

var
  Punto1: TPunto;
begin
  Punto1 := TPunto.Create;
  Punto1.Cargar;
  Punto1.ImprimirCuadrante;
  Punto1.Free;
  ReadLn;
end.

Problemas propuestos

  • Confeccionar una clase que represente un empleado. Definir los campos para almacenar su nombre y su sueldo. Confeccionar los métodos para la carga, otro para imprimir sus datos y por último uno que imprima un mensaje si debe pagar impuestos (si el sueldo supera a 3000)
  • Implementar la clase operaciones. Se deben cargar dos valores enteros, calcular su suma, resta, multiplicación y división, cada una en un método, imprimir dichos resultados.
Solución
program Proyecto128;

{$APPTYPE CONSOLE}

type
  TEmpleado = class
    Nombre: String;
    Sueldo: Double;
    procedure Cargar;
    procedure PagaImpuestos;
  end;

procedure TEmpleado.Cargar;
begin
  Write('Ingrese nombre:');
  ReadLn(Nombre);
  Write('Ingrese sueldo:');
  ReadLn(Sueldo);
end;

procedure TEmpleado.PagaImpuestos;
begin
  if sueldo >= 3000 then
    WriteLn('Debe abonar impuestos')
  else
    WriteLn('No paga impuestos');
end;


var
  Empleado1: TEmpleado;
begin
  Empleado1 := TEmpleado.Create;
  Empleado1.Cargar;
  Empleado1.PagaImpuestos;
  Empleado1.Free;
  ReadLn;
end.




program Proyecto129;

{$APPTYPE CONSOLE}

type
  TOperacion = class
    valor1: Integer;
    valor2: Integer;
    procedure Cargar;
    procedure Sumar;
    procedure Restar;
    procedure Multiplicar;
    procedure Dividir;
  end;

procedure TOperacion.Cargar;
begin
  Write('Ingrese primer valor:');
  ReadLn(valor1);
  Write('Ingrese segundo valor:');
  ReadLn(valor2);
end;

procedure TOperacion.Sumar;
var
  suma: Integer;
begin
  suma := Valor1 + Valor2;
  WriteLn('La suma es:', suma);
end;

procedure TOperacion.Restar;
var
  resta: Integer;
begin
  resta := Valor1 - Valor2;
  WriteLn('La resta es:', resta);
end;

procedure TOperacion.Multiplicar;
var
  multiplicacion: Integer;
begin
  multiplicacion := Valor1 * Valor2;
  WriteLn('La multiplicación es:', multiplicacion);
end;

procedure TOperacion.Dividir;
var
  division: Integer;
begin
  division := Valor1 div Valor2;
  WriteLn('La división es:', division);
end;


var
  Operacion1: TOperacion;
begin
  Operacion1 := TOperacion.Create;
  Operacion1.Cargar;
  Operacion1.Sumar;
  Operacion1.Restar;
  Operacion1.Multiplicar;
  Operacion1.Dividir;
  Operacion1.Free;
  ReadLn;
end.