Evolución de los Lenguajes de Programación 
y Programación Orientada a Objetos

 

 

Lenguajes de Bajo Nivel

Los primeros lenguajes de programación tienen sus orígenes en las primeras computadoras digitales. En ese entonces, los lenguajes eran un reflejo directo del hardware que controlaban. Poco a poco esto fue cambiando, de tal forma que la mayoría de los lenguajes contemporáneos son independientes de cualquier plataforma computacional.

A finales de los años cuarenta, el desarrollo incipiente de las computadoras, hizo adoptar dos conceptos fundamentales para su posterior desarrollo, cuya validez se mantiene hasta el día de hoy:

bola.gif (931 bytes) El sistema de numeración usado por las computadoras debe ser binario en lugar de decimal, dado que es más sencillo para los componentes electrónicos el tener que representar únicamente 2 estados.

bola.gif (931 bytes) La memoria de la computadora además de almacenar los datos de un programa, deberá almacenar el programa mismo.

En la memoria se almacenan datos enteros, a los cuales se les pueden aplicar operaciones aritméticas cuyos resultados son depositados en un registro llamado acumulador. Es posible modificar las localidades de memoria mediante una operación conocida como asignación. Las instrucciones del programa se toman de la memoria y se ejecutan de manera secuencial, a no ser que se encuentre una instrucción de salto, en cuyo caso la siguiente instrucción a ejecutar será tomada de la localidad de memoria indicada por dicha instrucción.

Los lenguajes de programación que la computadora puede entender de manera directa son conocidos como lenguajes de máquina.

De manera simplista, a cada instrucción de lenguaje de máquina se le asigna un código de operación, el cual es un simple número. Estos códigos de operación son almacenados en la memoria en su representación binaria.

Aunque para una máquina una serie de unos y ceros es suficiente para representar un código o instrucción, para los seres humanos resulta bastante incomprensible. Es por esto que surgió una variante de lenguaje de máquina conocido como lenguaje ensamblador, en donde los códigos de operación junto con otros elementos de un programa son reemplazados por nombres simbólicos.

Aunque el lenguaje ensamblador resulta críptico, es definitivamente más claro que su representación binaria. Sin embargo, este lenguaje aún conlleva una limitación más, y es el hecho de que cada modelo de computadora tiene un lenguaje de máquina distinto a los demás, por lo que los programas escritos para una máquina específica solamente funcionarán en ésta.

Lenguajes de Alto Nivel

Cuando las computadoras digitales se convirtieron en un producto comercial, se vio la necesidad de generar programas utilizando lenguajes más sencillos de comprender para el humano que el ensamblador.

Era evidente que para superar los problemas que existían en el desarrollo de los sistemas computacionales, la programación de éstos se debería hacer a un nivel superior que el impuesto por las máquinas. Es así que surge el concepto de lenguaje de alto nivel.

Los lenguajes de alto nivel ofrecen las siguientes ventajas sobre los lenguajes de bajo nivel:

Transportabilidad. Un lenguaje de alto nivel no es dependiente de una computadora específica; lo cual implica que los programas pueden correr en distintas computadoras habiendo el único requisito de que exista un traductor del lenguaje para la máquina en cuestión.

Fácil comprensión. Los programas escritos en lenguajes de alto nivel son más fáciles de escribir y entender. Esto generalmente permite una reducción del tiempo que el programador invierte en las fases de codificación, depuración y mantenimiento.

Los primeros lenguajes de alto nivel fueron diseñados para resolver problemas de índole numérico, por lo tanto tuvieron una tendencia hacia la representación de expresiones matemáticas.
Posteriormente las tendencias de diseño de Lenguajes de Programación cambiaron, dando mayor énfasis al cómo hacer las cosas; es decir, los algoritmos comenzaron a jugar el papel preponderante.
Finalmente, comenzando desde los años setenta y hasta la fecha, se ha manifestado una tendencia hacia lo que se conoce como abstracción de datos, que consiste en diseñar los sistemas de tal forma que gran parte de un programa puede ser especificado con independencia de la representación de los datos.

Los lenguajes de tercera generación (también llamados lenguajes de programación moderna o estructurada) están caracterizados por sus potentes posibilidades procedurales y de estructuración de datos. Los lenguajes de esta clase se pueden dividir en 3 grandes categorías:

bola.gif (931 bytes) Lenguajes de Alto Nivel de Propósito General,

bola.gif (931 bytes) Lenguajes de Alto Nivel Orientados a Objetos y

bola.gif (931 bytes) Lenguajes Especializados.

Los lenguajes especializados han sido diseñado para satisfacer los requisitos especiales y a menudo, tienen una formulación y una sintáxis únicas.

Programación Orientada a Objetos

La programación orientada a objetos es una filosofía de implementación en donde los programas se organizan como una colección cooperativa de objetos, cada uno de los cuales representa una instancia de alguna clase y cuyas clases pertenecen a una jerarquía de clases unidas a través de una relación de herencia.

Un lenguaje orientado a objetos debe soportar y encausar la programación orientada a objetos. Se debe hacer la distinción entre los lenguajes que promueven la programación orientada a objetos y aquéllos que simplemente la permiten. En teoría, se puede programar orientado a objetos en lenguajes que no fueron originalmente diseñados con ese propósito, pero el hacerlo constituye una labor extraordinaria.

Los conceptos generales más empleados en el modelo orientado a objetos son: abstracción, encapsulación y modularidad y en cuanto a programación son: objeto, clase, método, envío y recepción de mensajes, herencia y polimorfismo.

Parece ser universalmente aceptado que para que un lenguaje sea considerado orientado a objetos debe contar con por lo menos las siguientes tres propiedades: encapsulamiento, polimorfismo y herencia. Cada una de estas tres propiedades se describen brevemente a continuación.

Abstracción

Abstracción es la descripción o especificación simplificada de un sistema que hace énfasis en algunos detalles o propiedades y suprime otros. Una buena abstracción es aquella que hace énfasis en los detalles significativos y suprime los irrelevantes.

La abstracción debe enfocarse más en qué es un objeto y qué hace antes de pensar en la implementación. Otra característica de la abstracción es que un objeto puede abstraerse de diversas formas, dependiendo del observador.

Encapsulamiento

El encapsulamiento es la propiedad que los objetos tomaron prestada de sus primos los tipos de datos abstractos.

El objeto tiene dos caras. La primera cara, la interfase, es la que el objeto da al mundo exterior; es la que muestra lo que puede hacer, más no dice cómo lo hace. La otra cara, la implementación, es la que se encarga de hacer el trabajo y de mantener el estado del objeto. Solamente esta cara puede modificar el estado del objeto.

Al encapsular u ocultar información se separan los aspectos externos de un objeto (los accesibles para todos) de los detalles de implementación (los accesibles para nadie). Con esto se intenta lograr que al tener algún cambio en la implementación de un objeto no se tengan que modificar los programas que utilizan tal objeto.

Modularidad

La modularidad consiste en dividir un programa en partes llamadas módulos, las cuales pueden trabajarse por separado. En términos de programación, los módulos pueden compilarse por separado y la división no depende de un cierto número de líneas, mas bien, se trata de una división que se hace con el objetivo de integrar en un módulo un conjunto de procedimientos relacionados entre sí, junto con los datos que son manipulados por tales procedimientos. El objetivo de la modularidad es reducir el costo de elaboración de programas al poder dividir el trabajo entre varios programadores.

Estos conceptos no son exclusivos de la POO pues se han desarrollado desde la programación estructurada sólo que en ésta se pueden omitir, desde luego bajo la responsabilidad del programador, pues el hacerlo lleva a tener grandes programas en un solo archivo y sin estructura alguna, lo cual causa grandes pérdidas de tiempo al desear modificar tal programa.

Objetos y Clases

A pesar de que el punto central en esta nueva metodología de programación es el concepto de objeto, resulta difícil tratar de definirlo. En un diccionario se puede obtener la siguiente definición:

" Un objeto es cualquier cosa que se ofrece a la vista y afecta los sentidos. Es una entidad tangible que exhibe algún comportamiento bien definido."

En términos de programación, un objeto no es algo necesariamente tangible (por ejemplo: un proceso). Lo que sí puede decirse de todo objeto es que tiene estado, comportamiento e identidad.

El estado de un objeto abarca todas las propiedades o características distintivas del mismo y los valores de cada una de estas propiedades son las variables que sirven para describir tal objeto.

El comportamiento es la forma en cómo actúa o reacciona un objeto en términos de cambios de estado, envío y recepción de mensajes. Está formado por la definición de las operaciones (funciones y procedimientos) que puede realizar este objeto. Los tipos más comunes de operaciones, o mejor dicho, de métodos son: modificar, seleccionar, iterar, construir y destruir.

El conjunto de operaciones que un objeto puede realizar sobre otro, se conoce como protocolo.

Identidad es la propiedad de un objeto que lo distingue de todos los demás. En un programa, normalmente se trata de un identificador.

En resumen, un objeto es un conjunto de localidades en memoria con un conjunto de subprogramas (en POO se conocen como métodos) que definen su comportamiento y un identificador asociado.

Lo más común es que un programa tenga más de un objeto con propiedades y comportamientos similares, así que en lugar de repetir la definición de un objeto se agrupan las características comúnes de los objetos en una clase.

La clase representa la esencia del objeto y el objeto es una entidad que existe en el tiempo y el espacio. El objeto se define también como instancia de la clase a la que pertenece.

La clase tiene dos vistas: la exterior, en la cual se hace énfasis en la abstracción y se oculta la estructura y secretos de comportamiento; y la vista interior o implementación. Aquí se nota que es indispensable hacer uso del concepto de encapsulamiento.

Herencia

Cada objeto es una instancia de una clase. Una clase es parecida a lo que se ha manejado como tipo de dato abstracto: es una descripción abstracta de los datos y comportamiento que comparten objetos similares. La herencia es la manera de establecer relaciones entre las distintas clases que conforman a un sistema orientado a objetos. Con la herencia se establecen jerarquías del tipo "es un", en donde una subclase hereda la estructura y comportamiento de una o más superclases más generalizadas. Típicamente una subclase especializa a su superclase al aumentar o redefinir la funcionalidad de esta última.

Existen lenguajes que solamente permiten que las subclases tengan un solo antecesor directo, mientras que otros permiten que tengan más de uno. A los primeros se les conoce como lenguajes con herencia simple, mientras que a estos últimos se les llama lenguajes con herencia múltiple.

La herencia es la contribución más importante de la POO, pues mediante este mecanismo es posible lograr la principal meta de la POO que es la de la reutilización de código. El atractivo de la herencia consiste en que al requerir un nuevo componente para un programa, en lugar de diseñarlo y construirlo de cero, se busca una clase que proporcione una funcionalidad lo más parecido a la deseada; una vez encontrada, se crea una nueva clase que herede de ésta, y se agregan y/o modifican únicamente aquellos detalles que sean precisos para obtener el componente que originalmente se estaba necesitando. Una subclase define el comportamiento de un conjunto de objetos que heredan algunas de las características de la clase padre, en este sentido se dice que la subclase es una especialización de la clase padre.

Lo anterior suena bien, pero no es tan sencillo de lograr. Para ello se necesita una jerarquía bien diseñada y para lograr un buen diseño normalmente se requiere de mucha experiencia; pero una vez logrado, los beneficios son realmente compensadores.

Poliformismo

Otro de los mecanismos aportados por la POO es el de polimorfismo, el cual es la capacidad de tener métodos con el mismo nombre pero que su implementación sea diferente.

Una forma de polimorfismo en POO, se da al usar un operador para aplicarlo a elementos de diferente tipo. Por ejemplo, al pretender usar enteros, reales o complejos, se emplea el mismo símbolo " + ", esto se conoce como sobrecarga de operadores. En este caso, el compilador se encarga de determinar cuál es el método que se estará invocando de acuerdo a los objetos involucrados en la operación.

En un programa con objetos las acciones ocurren cuando se les manda mensajes a los objetos. El polimorfismo es una característica que permite a distintos objetos responder al mismo mensaje de manera única. El polimorfismo permite utilizar clases completamente nuevas en aplicaciones existentes, siendo el único requisito de las clases el que implementen los mensajes requeridos para la aplicación.

Un nombre de variable puede contener en diferentes momentos referencias a objetos de distintas clases que tienen un mismo antecesor. Cuando esto ocurre, dicho nombre de variable puede responder a un conjunto de operaciones comúnes de diferentes maneras.