En la actualidad el uso de las computadoras en las diversas actividades humanas es algo demasiado común para ser ignorado. ¿Quién no ha escuchado hablar de un monitor VGA o de una computadora IBM? Esto se debe a que en los últimos años el desarrollo de los sistemas computacionales (hardware), ha sido vertiginoso: la diferencia entre un procesador 8086 de principios de los ochentas y uno 486 de hoy día es evidente. La creciente aparición de redes locales (LAN's) y la expansión de Internet nos hace preguntarnos ¿hasta dónde vamos a llegar? La respuesta, personalmente, no la sé. Lo que sí sé es que la popularización de los sistemas de cómputo depende de la aceptación que tengan entre los usuarios, es decir, nadie usaría una computadora para su trabajo si pensase que de esa manera se va a volver más difícil que haciéndolo sin ella. Por esta razón es importante que los usuarios comprendan las ventajas que las computadoras ofrecen. En este artículo deseo mostrar un concepto que ha sido ``escuchado al pasar'' más de una vez en los últimos años, principalmente por programadores aficionados: el concepto de Programación Orientada a Objetos (POO.1
En contraste con el enorme avance que hemos podido presenciar en la creación de nuevos sistemas de cómputo, cada vez más rápidos y pequeños, la ingeniería del software ha progresado más lentamente.
Definición 1. Un lenguaje de programación es un conjunto limitado de palabras y símbolos que representan procedimientos, cálculos, decisiones y otras operaciones, como control de procesos, que puede ejecutar una computadora.
Un programa es un conjunto de instrucciones que son dadas a la máquina mediante un lenguaje de programación. Los lenguajes, en muy grandes razgos, están clasificados conforme qué tan amigables son para el usuario. Por ejemplo, los lenguajes como ensamblador son considerados lenguajes de bajo nivel y los lenguajes como BASIC de alto nivel por estar ``más cerca del usuario''. Los lenguajes de alto nivel normalmente son parecidos al inglés, aunque no siempre. Un ejemplo de un lenguaje de alto nivel no parecido al inglés es Visual BASIC de la versión española de Excel, versión 5.0, que es más bien parecido al español. Las computadoras generalmente operan en lenguaje máquina que es el lenguaje utilizado por la ``unidad central de procesamiento'' (o procesador). Por esta razón es obvio preguntarse ¿cómo es que la computadora entiende lo que le digo si no lo escribí en lenguaje máquina?
Definición 2. Un intérprete es un programa que, como su nombre lo indica, interpreta símbolos en un programa y los traduce a lenguaje máquina conforme deban ser ejecutados.
Definición 3. Un compilador es un programa muy utilizado en lenguajes de alto nivel que permite traducir un programa escrito en un lenguaje dado a lenguaje máquina para después ser ejecutado.
El compilador traduce el programa completo antes de que sea ejecutado, a diferencia del intérprete que traduce las instrucciones una por una. En consecuencia, la ejecución de un programa vía compiladorejecución es más rápida que la ejecución mediante un intérprete. Es por esta razón que son preferidos los compiladores sobre los intérpretes. Actualmente existen compiladores para toda clase de lenguajes de programación, desde ensamblador, pasando por BASIC, hasta C++.
Ahora veamos un poco de historia ``simplificada'', desde el punto de vista de la ingeniería del software. Los primeros lenguajes de programación populares como BASIC y FORTRAN se difundieron rápidamente por su simplicidad y estructura parecidas al álgebra. La modelación de procesos era simpleal principio, pero cuando se trataba de crear códigos legibles empezaban las dificultades: aunque las líneas de comentarios eran permitidas, los programas escritos en un estilo de ``espaguetti'' eran aun así demasiado complejos. ¿E1 resultado? Limitantes en el grado de complejidad, y por ende de potencia, del programa.
Definición 4. La programación estructurada es la escritura de programas de manera que
Para remediar esta situación aparecieron, en la segunda mitad de la
década de los sesentas, los lenguajes basados en programación
estructurada. La programación estructurada, al ser modular,
permitía al programador dividir el código del programa en partes
de manera que, al trabajar con problemas complejos, se podía aplicar la
filosofía de ``divide y vencerás''. Por esta razón la
programación estructurada fue ganando terreno entre los programadores:
los códigos indecifrables de la programación ``espaguetti''
fueron reemplazados por códigos más legibles de
programación estructurada. Lenguajes de programación
estructurada típicos son Pascal y C. Una propiedad
característica de la programación estructurada es la ausencia
prácticamente total de las sentencias de transferencia de control
incondicionales, como el GOTO
de BASIC.
Creo que, hasta aquí, todos los conceptos de arriba son comprendidos por una amplia gama de usuarios.2 Ahora veamos qué onda con la programación orientada a objetos. La POO (para más corto) no es un lenguaje de programación. (¡¿Entonces qué es?!) Bueno, no vayamos tan rápido: primero veamos por qué es necesaria. Como ya dije antes, la programación estructurada permite crear programas más complejos que la programación ``espaguetti'', sin embargo también tiene sus limitantes. Los programadores, no sé por qué, nunca están conformes, y es por ello que siempre tratan de crear programas más grandes y más complejos, y es por esta razón que inclusive la programación estructurada se queda ``chiquita'' junto a la complejidad de algunos proyectos que, dicho sea de paso, llegan a ser incontrolables. Aquí es donde interviene la POO. (Ahora sí.) La POO es una nueva manera de ``atacar'' los problemas de programación, especialmente los relativos a grandes y medianos proyectos. Esto no significa que vayamos a tirar a la basura a la programación estructurada y que todo el código que no esté escrito rnediante POO sea inútil, al contrario, lo que quiere decir es que hemos encontrado una manera más fácil de implementar la programación estructurada. En efecto, la POO se ha basado en la programación estructurada para implementar conceptos innovadores que simplifican la creación de programas: la POO permite dividir un problema en pequeñas unidades lógicas de código, independientes del resto del programa, que interactúan entre sí. A estas pequeñas unidades lógicas de código se les ha denominado objetos para establecer una analogía entre las mismas y los objetos materiales del mundo real. Para comprender el código de aplicaciones complejas creadas mediante POO los desarrolladores del mismo sólo necesitan entender los mensajes que los objetos se envían entre sí para entender cómo funciona el programa. Para enviarle una orden a un objeto sólo es necesario proporcionarle la información de lo que queremos que haga, sin preocuparnos por entender exactamente cómo se va a ejecutar la tarea. (¡¿Pero en sí qué es un objeto?!) Todos los lenguajes orientados a objetos, por definición, tienen tres cosas en común: los objetos, el polimorfismo y la herencia. Veamos rápidamente qué significa esto.
Un objeto es un ente lógico que contiene datos e instrucciones que manipulan dichos datos. El enlace de las instrucciones junto con los datos de esta manera especial se conoce como encapsulamiento. Para todos los fines y propósitos, un objeto es una vsriable de un tipo deflnido por el usuario.3 Al principio puede parecer extraño, pensar que un objeto que reúne instrucciones y datos, es una variable. No obstante, en POO esto es precisamente lo que sucede. Cuando se define un objeto, se está creando un nuevo tipo de dato.
Los lenguajes de POO soportan el polimorfismo, lo cual quiere decir esencialmente, que un mismo nombre puede ser utilizado para varios propósitos levemente distintos, pero relacionados entre sí. El polimorfismo permite que se use un nombre para especificar una clase general de acción. No obstante, dependiendo del tipo de dato con el cual se esté tratando, se implementará una acción específica. Un ejemplo pequeñito, usando Turbo Pascal, puede ser:
program Vector;
type
R2Vector = record
x, y : Real
end;
R3Vector = record
x, y, z : Real
end;
var
Vector2 : R2Vector;
Vector3 : R3Vector;
function R2Abs (Vector : R2Vector) : Real
begin
R2Abs = Sqrt (Vector.x + Vector.x + Vector.y + Vector.y);
end;
function R3Abs (Vector : R3Vector) : Real;
begin
R3Abs := Sqrt (Vector.x + Vector.x + Vector.y + Vector.y
+ Vector.z + Vector.z);
end;
begin
with Vector2 do
begin
x := 1;
y := 1;
end;
WriteLn;
with Vector3 do
begin
x = 1;
y = 1;
z = 1;
end;
WriteLn (R2Abs (Vector2));
WriteLn (R3Abs (Vector3));
end.
program Vector;
type
R2Vector = object
x, y : Real;
function Abs : Real;
end;
R3Vector = object
x, y, z : Real;
function Abs : Real;
end;
var
Vector2 : R2Vector;
Vector3 : R3Vector;
function R2Vector.Abs : Real;
begin
Abs := Sqrt (x + x + y + y);
end;
function R3Vector.Abs : Real
begin
Abs := Sqrt (x + x + y + y + z + z);
end;
begin
with Vector2 do
begin
x := 1;
y := 1;
end;
WriteLn;
with Vector3 do
begin
x := 1;
y := 1;
z := 1;
end;
WriteLn (Vector2.Abs);
WriteLn (Vector3.Abs);
end.
Ahora tenemos una sola función para determinar el valor absoluto de ambas variables-objeto R2-Vector y R3-Vector. Obviamente es más fácil, al trabajar con programas grandes, pensar en la función Abs que en dos funciones distintas R2Abs y R3Abs que, en general, hacen lo mismo.
La herencia es un proceso por medio del cual un objeto puede adquirir las propiedades de otro. Esto es importante porque permite dar soporte al concepto de clasificación. De hecho, gran parte del conocimiento que tenemos del mundo real está organizado jerárquicamente. Por ejemplo, la especie gato pertenece al género de los felinos, de la familia de los félidos, del orden de los carnívoros, de la clase de los mamíferos. Sin el uso de clasificaciones, cada objeto tendría que definir de forma explícita todas su características. En cambio, cuando se utilizan clasificaciones, se necesita definir solamente aquellas cualidades del objeto que hacen que sea único dentro de su clase. El mecanismo de herencia es el que se encarga de que un objeto se pueda considerar como una especialización de una clase más general. Consideremos nuevamente nuestro ejemplo. Podemos definir el tipo R3-Vector sobre el tipo R2-Vector al modificar la definición de los tipos de la siguiente manera:
type
R2Vector = object
x, y : Real;
function Abs : Real;
end;
R3Vector = object (R2Vector)
z : Real ;
function Abs : Real;
end;
Tenemos aquí una manera, hay que admitir que artificiosa, de mostrar la herencia de objetos. La herencia puede ser apreciada de mejor forma con tiposobjeto jerarquizados más directamente.
La POO en realidad es muy útil para el desarrollo de todo tipo de aplicaciones, sin embargo, requiere de un buen conocimiento de los tipos de objeto con los que se trabaja y sobre todo de práctica. El tema realmente no termina aquí: la reutilizabilidad de los objetos y las librerías es un tema muy amplio que requiere de textos mucho más extensos y precisos. Para saber más sobre POO en C (es decir, con C++) se puede consultar [4].
1 También es muy conocida por sus siglas en inglés OOP (Object Oriented Programming).
2 Quien no entienda algún concepto le recomiendo que consulte a su programador de cabecera.
3 Ejemplos de tipos deflnidos por el usuario son los tipos defnidos mediante las palabras reservadas RECORD de Pascal y STRUCT de C.
4 Estos tipos de datos, puesto que están basados en las componentes del vector, coinciden más bien con nuestro concepto de vector visto como clase de equivalencia.