A medida que las aplicaciones se van haciendo cada vez más complejas, el mantenimiento del código también se hace más difícil, a pesar de que esté correctamente estructurado en funciones y procedimientos y dividido en múltiples módulos. En un programa complejo, (solo la programación del entorno bajo Windows ya hace de cualquier programa un programa complejo) es fácil perderse entre la cantidad de procedimientos, funciones y distintas variables distribuidas en multitud de unidades, con lo que hay grandes probabilidades de errores al pasar parámetros incorrectos, modificar variables indebidas, etc.
Muchos de estos problemas encuentran su solución en la programación orientada a objetos (POO, u OOP en inglés), que no es más que una evolución de los lenguajes estructurados hacia una simplificación del mantenimiento del código y unas mayores posibilidades de reutilización de éste.
Object Pascal cuenta con todas las características de un lenguaje orientado a objetos, como son la encapsulación, la herencia y el polimorfismo.
Un objeto es parecido a un registro (record), pero se caracteriza porque además de contener miembros de distintos tipos, como números, cadenas, punteros, etc., también es capaz de contener definiciones de procedimientos y funciones.
Para definir un nuevo tipo de objetos utilizamos
una estructura similar a la de un registro, con la diferencia de que cambiaremos
la palabra Record por Class (como en C++). Tras esta cabecera dispondremos
los miembros que formarán parte del tipo, que además de variables
pueden ser funciones y procedimientos. A los procedimientos y funciones
que forman parte de la definición de un objeto se les llama genéricamente
métodos. Una clase lo más simple posible podría definirse
como:
TPunto = Class {Objeto TPunto}
x, y: Integer; {Coordenadas
y color del punto}
Color: Byte;
Procedure DibujaPto; {Método
que dibujará el punto}
End;
Para utilizar el objeto anterior, definimos una variable de tipo TPunto:
Var
Punto: TPunto;
Pero en realidad Punto es una referencia al tipo
de objeto (un puntero), por lo que deberemos crear el objeto dinámicamente
y liberarlo después de usado:
new (Punto) {Crea objeto}
Punto.x:=25; {Uso del objeto}
Punto.y:=33;
Punto.color:=0;
Punto.DibujaPto;
dispose (Punto) {Libera objeto}
Normalmente, los objetos ya llevan sus propios métodos de creación y liberación. Los métodos denominados "Constructores" (Objeto.Create) y "Destructores" (Objeto.Free) sirven para crear e inicializar el objeto, y para liberarlo, por lo que no se suele usar new y dispose (o getmem y freemem, o similares).
Si tenemos un componente correctamente diseñado, los datos que se declaren en el interior del objeto sólo serán manipulados por los métodos de ese objeto, que estarán precisamente especializados en la manipulación de los miembros del objeto. A esta característica se le conoce como encapsulación.
Para gestionar de forma correcta la encapsulación, se pueden dividir los miembros del objeto en partes públicas (Public), privadas (Private) y protegidas (Protected). Las declaraciones existentes en el grupo Public serán públicas, y por lo tanto otros programas o módulos podrán acceder a los miembros. En el apartado Private podemos incluir la declaración de todos aquellos miembros que no deseamos poner a disposición de terceros. Todas las variables y métodos que incluyamos en esta parte serán accesibles sólo desde el propio objeto, es decir, desde los métodos del objeto. Por último tenemos el apartado Protected, que da paso a la parte protegida del objeto, que es un paso intermedio de las dos anteriores. Los identificadores creados en la parte protegida no serán accesibles desde otros programas o módulos, pero sí tendrán acceso a ellos aquellos objetos que sean derivados del nuestro.
Un ejemplo de como podríamos dividir el objeto TPunto en partes
públicas y privadas podría ser:
TPunto = Class {Objeto TPunto}
Private {Parte privada}
x, y: Integer; {Coordenadas
y color del punto}
Color: Byte;
Public {Parte pública}
Procedure Inicializa (PX, PY: Integer;
PColor: Byte);
Procedure DibujaPto; {Método
que dibujará el punto}
End;
De esta forma no podemos acceder directamente a las coordenadas y al color, sino que hacemos la asignación mediante el método "Inicializa", que sería el encargado de asignar las coordenadas y el color al objeto a través de los parámetros pasados.
Otro de los pilares de la OOP es la herencia. Esta característica nos permitirá no tener que escribir completamente el código del componente que necesitemos, sino derivarlo de otro componente que tenga las características básicas que necesitemos, y adaptarlo a nuestra necesidad concreta añadiéndole sólo las características adicionales que necesitemos. Además es posible ir ampliando en sucesivas derivaciones un objeto haciéndolo cada vez más complejo y adecuado a una utilidad particular.
Por ejemplo podemos crear un objeto TRectangulo derivado
del TPunto ya definido, que se ocupe de dibujar un rectángulo (lo
hará dibujando sucesivos puntos con los miembros heredados):
TRectangulo = Class(TPunto) {derivado de TPunto}
Private {Dimensiones y color del rectángulo}
left, top, width, height, color: integer;
Public
Procedure Ini(izda,arriba,ancho,alto,colorR:
integer);
Procedure DibujaRectangulo;
End;
Para implementar el método DibujaRectangulo
podemos usar los métodos y miembros de la clase ancestro TPunto
porque ya son parte del propio objeto TRectangulo:
Procedure TRectangulo.DibujaRectangulo;
var
cont,cont2:integer;
begin
for cont:=left to left+width do
{Dibuja rectangulo}
for cont2:=top to top+height
do
begin
Inicializa(cont,cont2,100);
{Dibuja punto}
DibujaPto;
end;
end;
Si un objeto no se deriva de ningún otro (como el objeto TPunto del ejemplo), en Object Pascal se derivará por defecto del tipo TObject, con lo cual podemos usar en cualquier objeto los métodos Create, Free, etc. y además al tener una raíz común, podemos tratar a los objetos polimórficamente. Polimorfismo es la capacidad de un objeto de corresponder a distintos tipos, esto es, por ejemplo un objeto TPunto podría apuntar a un TRectangulo, o a un TCirculo (si lo hubiésemos definido como derivado de TPunto), de forma que podemos tratar objetos de distinto tipo genéricamente. Por ejemplo podríamos tener un array del tipo TPunto que contuviese objetos TRectangulo y TCirculo.