Introducción a la programación orientada a objetos
La programación orientada a objetos (POO u OOP en inglés) es una filosofía de programación que se basa en considerar que los programas están compuestos de unas unidades llamadas objetos, y las acciones que se ejecutan por tanto están relacionadas con ellos. Un objeto es un tipo de datos especial que reúne una estructura de datos (lo que hemos llamado "tipo definido por el usuario"), así como a los procedimientos que trabajan con estos datos. A partir de un objeto se pueden derivar otros objetos más especializados (lo que se conoce como herencia), situación que permite modelar ciertas jerarquías que se dan en la vida real.
Pongamos, por ejemplo, que queremos realizar un programa que trate sobre seres vivos. Crearemos un objeto "ser vivo" que tendrá una serie de datos como "tiempo medio de vida" y unos métodos (se llama método a la función o procedimiento que trabaja con los datos de un objeto) que permitirán manipular estos datos. Ahora resulta que dentro de los seres vivos tenemos a las plantas y a los animales. Estas dos entidades son una especialización del objeto primitivo "ser vivo". Cada uno de ellos es un ser vivo, pero tienen detalles que les hacen diferentes entre sí y por eso tienen que ser tratados de forma distinta. Dentro de los animales, a su vez, tenemos vertebrados e invertebrados. De nuevo, una especialización, que tiene todas las características de los antecesores y algunas propias. Dentro de los vertebrados, tenemos peces, anfibios, reptiles, aves y mamíferos: una nueva especialización.
Vamos a poner ahora un ejemplo más cercano al mundo de la programación. Supongamos que queremos hacer un programa de dibujo. Probablemente querremos poner elementos como ventanas, botones, menús, barras de desplazamiento... y otros.
Pensando un poco, todo esto se puede llevar a cabo con lo que uno sabe de programación procedural, sin embargo, ¿no sería más fácil (por ejemplo) tener un objeto VENTANA?
Este objeto podría tener las siguientes variables: coordenadas del rectángulo en el que se va a dibujar, estilo de dibujo... y podría tener unas funciones que, leyendo estos datos, dibujaran la ventana, cambiaran su tamaño, la cerraran...
La ventaja que tendría esto es que ya podemos crear cuantas ventanas queramos en nuestro programa, puesto que las funciones asociadas tendrían en cuenta las variables de este objeto y no deberíamos pasárselas como argumentos.
Igual que con la ventana, se podría crear el objeto BOTON, a partir del objeto ventana (pues a fin de cuentas se trata de un rectángulo que debemos de pintar), que podría tener como variables las coordenadas (heredadas de la ventana), si está pulsado o no, si está el ratón sobre él o no... y unas funciones que lo dibujaran, lo dibujaran pulsado, detectaran si se ha pulsado el botón, hicieran algo si ese botón se ha pulsado...
Las funciones de un objeto nos proporcionan un interfaz para manejarlo: no necesitamos saber cómo está hecho un objeto para poder utilizarlo.
Cuando creamos un objeto, en realidad estamos creando un 'molde', que recibe el nombre de clase, en el que especificamos todo lo que se puede hacer con los objetos (ahora sí) que utilicen ese molde. Es decir, en realidad lo que uno hace es crear una clase. Cuando va a utilizarla, crea instancias de la clase, y a estas instancias es a lo que se le llama objeto.
No vamos a dar una notación rigurosa para la definición de clases puesto que esto es sólo una introducción, y sobre POO se puede hablar bastante. Lo que vamos a dar es una pequeña orientación sobre cómo emplear la notación de acceso a las partes de un objeto, pues el lector puede así a partir de aquí empezar a programar en un entorno como sería Delphi, sin saber mucho sobre POO, pero sabiendo lo suficiente como para emplear los objetos que Delphi pone a su disposición.
Por ejemplo, en el caso de la ventana, podríamos definir una clase de la manera siguiente:
CLASE Ventana
VARIABLES
x0, y0, x1, y1: ENTEROS
FUNCIONES
Inicializar_Coordenadas()
Dibujar_Ventana()
Mover_Ventana()
FIN CLASE Ventana
Y cuando queramos tener una (o varias) ventanas, las declararíamos (instanciaríamos) como cualquier otro tipo de variable:
Ventana Ventana1, Ventana2;
A la hora de acceder a las variables que tiene cada una de estas ventanas (cuyos valores son distintos para cada ventana) o de utilizar las funciones de cada una de estas ventanas, habría que poner:
Ventana1.x0 = 3;
Ventana2.Dibujar_Ventana();
es decir:
Objeto.Variable
Objeto.Funcion(argumentos)
En la definición de una clase se distinguen las partes privadas de las partes públicas. Un elemento privado de una clase es aquel que únicamente puede modificarse/emplearse desde la propia clase. Si, en el ejemplo de la clase Ventana, declaramos las variables x0, y0, x1, y1 como privadas, sólo desde el código de las funciones de la clase Ventana se podría tener acceso/modificar estos valores, mientras que desde cualquier otro punto (función principal del programa, otras funciones del programa) obtendríamos un error al intentar acceder a estas variables. La motivación de esto es la siguiente: cuando diseñamos un objeto, existen partes del mismo que no deben ser modificadas más que por el propio objeto, pues cualquier acceso desde otra parte del programa podría causar comportamientos extraños. Así, declarando ciertas partes del objeto como privadas, se previenen los accesos indebidos que podrían provocar problemas.
Todo lo contrario sucede con las partes públicas de un objeto: son variables o funciones que pueden ser accedidas desde cualquier punto del programa. Esto será así porque su uso/modificación en cualquier parte del programa no desencadenará comportamientos extraños en el objeto.
Existen, además, dos funciones especiales llamadas constructor y destructor. La misión del constructor es inicializar aquellas variables que sea necesario tener inicializadas (muy probablemente pasando dichos valores al constructor) así como poner a punto algunos comportamientos necesarios, quizá llamando a alguna de las funciones de la clase, o bien reservar memoria en el caso de que trate con variables dinámicas. La misión del destructor será, principalmente, la de liberar todos los recursos que hayan podido reservarse por el objeto.