>>
Lección 02


La programación orientada a objetos.


La programación es un actividad humana que se ha desarrollado casi enteramente durante la segunda mitad del siglo XX.

Por tanto podemos suponer que aún está en sus orígenes y que el futuro traerá todavía grandes adelantos técnicos y teóricos que mejorarán sus resultados.

En su corta historia, la programación ha sufrido ya dos pequeñas revoluciones. La primera, protagonizada principalmente por Wirth, Dijstra y de forma menos teórica pero quizás con más impacto por Kernighan y Ritchie. Es lo que se denomina la programación estructurada.

Los primeros lenguajes de programación eran simplemente intrucciones que se le podían dar a un autómata como una computadora, para que realizara ciertas operaciones. Así un programa no era sino una lista de instrucciones encaminadas a realizar algún cálculo.

A medida que las computadoras fueron haciéndose más sofisticadas, sus lenguajes propios o lenguajes de máquina iban cambiando y surgió la necesidad de crear unos lenguajes intermedios que cualquier usuario pudiera aprender y que no dependieran de la máquina concreta en la que se iban a ejecutar los programas. Así surgieron varios lenguajes que se hicieron famosos y también surgieron los primeros compiladores. Un compilador es un programa que traduce las instrucciones de un lenguaje más o menos humano, a las de una máquina.

La aparición de estos lenguajes intermedios y sus compiladores marca el comienzo de la programación como una nueva ciencia.

El tremendo éxito que las computadoras fueron teniendo a lo largo de los años 60 fue llevando a la creación de programas cada vez más complicados que llegaban a tener cientos de miles de líneas de código.

Hacer correcciones a este tipo de programas y agregarles mejoras se fue convirtiendo en una labor imposible. Ante este problema que amenazaba con convertir las computadoras en máquinas estériles, surgió un grupo de científicos de la computación, de los cuales Dijstra y Wirth son dos de los más destacados, que fueron proponiendo una serie de ideas que llevaron a la creación de un nuevo concepto: la programación estructurada.

En pocas palabras, la programación estructurada propone dos ideas básicas: no repetir código y proteger las variables que una parte del programa usa de que sean modificadas accidentalmente por otras partes del programa.

Una sola repetición de código es fuente probable de errores y dificulta el mantenimiento de un programa. Para no repetir código hay que escribir funciones o procedimientos que se encarguen de realizar siempre lo que las diferentes repeticiones realizarían.

Para proteger las variables hay que desarrollar lenguajes que permitan definir variables locales, es decir, que sólo pueden ser utilizadas dentro de una función o procedimiento. De esta manera ninguna otra función del programa puede cambiarla.

Como consecuencia de estas ideas disminuye considerablemente el tamaño de los programas y éstos se hacen más confiables y más fáciles de corregir o mejorar.

Para facilitar la programación estructurada, aparecen nuevos lenguajes, notoriamente el Pascal y el C, que son los dos lenguajes de programación estructurada más conocidos.

Wirth, creador del Pascal, nunca quedó satisfecho con este lenguaje. En su constante análisis de los hábitos de programación encontraba que todavía había muchas cosas que mejorar. Una década después del Pascal publicó otro lenguaje llamado Modula2 en el que presentaban algunas de sus ideas más recientes. Los módulos del Modula2 pretendían representar objetos o clases. Dentro de un módulo se definían variables que representaban el estado del objeto y se definían procedimientos que describían su comportamiento. Las variables y los procedimientos de un módulo podían ser de uso público o privado, exclusivo de la clase, según lo decidiera el creador del módulo. Con todo esto se mejoraba notoriamente sobre el Pascal donde el concepto de procedimientos o funciones privadas no existía y se daba un paso importante hacia la programación de clases o programación orientada a objetos.

Otros científicos experimentaron ideas similares creando diversos lenguajes de programación orientada a objetos como Smalltalk. En ellos se experimentó con otras ideas útiles como la definición de subclases que heredan las propiedades de su superclase o sea de la clase de la que se derivan, pero agregando variables y funciones nuevas. También surgió una idea que ayudaría a evitar los difíciles problemas que surgían en el manejo de la memoria dinámica: los constructores y destructores de objetos.

A principios de los 90 se popularizó un nuevo lenguaje orientado a objetos que se parecía mucho a C. Se trata del C++ creado por Stroustroup. La idea de Stroustrup fue crear un lenguaje orientado a objetos que heredara prácticamente toda la sintaxis y posibilidades del lenguaje que en ese momento era más popular entre los programadores, el C. Este truco ayudó a popularizar la programación orientada a objetos y preparó el camino para la aparición del Java.

Los creadores de Java aprendieron bien la lección de Stroustroup. Un nuevo lenguaje para tener éxito debía ser muy parecido al que en el momento de su lanzamiento fuese el más popular. Así, Java se creó con un gran parecido al C++. Pero Java es un lenguaje más puro de programación orientada a objetos, conserva un poco del C original, pero se parece más al C++. Puede verse al C++ como un paso intermedio en la transición de la programación estructurada del C a la programación orientada a objetos más pura del Java. Actualmente (agosto de 2001) está apareciendo un nuevo lenguaje de Microsoft llamado C# (C sharp) que promete ser aún más puro como lenguaje orientado a objetos, adopta prácticamente todas las mejoras de Java y agrega algunas nuevas. Habrá que estar pendientes de su publicación y de sus posibles ventajas y probables desventajas (en computación nunca se gana algo sin perder otra cosa, lo importante es que en promedio las cosas vayan mejorando).

Java tiene otras virtudes aparte de ser un excelente lenguaje de programación orientada a objetos, virtudes que lo hacen el lenguaje de programación preferido para internet. Pero también tiene la peculiaridad de ser el lenguaje orientado a objetos más popular del momento (2001, antes del lanzamiento de C#).

Las siguientes secciones de esta lección explican el conceptos de clase, cómo se representa este concepto en Java y las demás características que hacen de Java un lenguaje orientado a objetos.


Clases y objetos.


El concepto de clase es el más importante del lenguaje Java y el que lo convierte en un lenguaje de programación orientada a objetos.

Todo lo que se programa en Java debe estar dentro de una clase. Un programa es una clase. Por ejemplo, el sencillo programa HelloWorld que vimos en la lección 01 tiene este código java:

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

en el que puede verse que todo el programa está dentro de una clase llamada "HelloWorld". Dentro de la clase hay un único método llamado "main" que es el que se ejecuta cuando el programa arranca. Esta forma de programar con clases que contienen métodos y variables (HelloWorld no usa variables, pero podría usarlas) es a lo que se ha llegado después de varios años de experiencia en la programación orientada a objetos.

La palabra clase viene del concepto "clase de objetos". Una clase consta de (a) un conjunto de variables que representa el estado de un objeto y (b) un conjunto de métodos que definen el funcionamiento y las operaciones que pueden realizarse sobre los objetos de esa clase.

Como ejemplo consideremos el caso de un punto en el plano cartesiano que puede desplazarse a la izquierda, a la derecha, hacia arriba y hacia abajo.

El estado de este punto queda determinado por dos variables:

X, la abscisa al origen
Y, la ordenada al origen

Además de las variables X e Y hay que incluir los métodos que definen el "funcionamiento" de la clase y además conviene agregar otros métodos auxiliares.

He aquí una definición de la clase punto.

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

A continuación explicamos detalladamente el significado de cada una de las variables y los métodos de esta clase.

X e Y aparecen definidas como variables. En la lección 03 se estudian todos los tipos de variables del lenguaje Java. Los métodos setX y setY sirven para fijar los valores de X e Y, mientras que getX y getY sirven para obtener los valores de estas variables. Los métodos moveLeft, moveRight, moveUp y moveDown definen el "funcionamiento" de la clase punto, es decir, las operaciones que se pueden realizar sobre un punto.

Los métodos setX y setY admiten un párametro cada uno. El valor de ese parámetro se utiliza para fijar el valor de la abscisa del punto y el valor de la ordenada, respectivamente. Los métodos getX y getY no aceptan parámetros. Los métodos moveLeft, moveRight, moveUp y moveDown aceptan un solo parámetro que se interpreta como la magnitud del desplazamiento que se realiza.

Todos los métodos, excepto getX y getY, tienen valor de devolución "void" (vacío), lo que quiere decir que no devuelven valor alguno. Los métodos getX y getY devuelven un valor de tipo "int", es decir, un número entero.

El último "método" que tiene el mismo nombre que la clase y no tiene valor de devolución, ni siquiera "void", es un constructor. Los constuctores se estudian en la siguiente sección.


Creación de objetos.


Para crear un objeto nuevo en Java se antepone la palabra new a un constructor de la clase del objeto que se desea crear.

Por ejemplo, para crear un punto con coordenadas (2,3) basta escribirlas dos líneas:

punto p;
p=new punto(2,3);

La primera línea simplemente declara que p es una variable de tipo punto. La segunda línea es la que en realidad "crea" el nuevo objeto p. Los objetos de una clase se llaman también instancias de la clase.

Toda clase tiene al menos un constuctor. Los constructores pueden tener uno o varios parámetros, y pueden no tener parámetros. Si en una clase no se define explícitamente un constructor, entonces tiene sólo un constructor sin parámetros.

Cuando se crea un objeto en realidad se aparta espacio de memoria para contener las variables del objeto. Un constructor suele además inicializar los valores de las variables del objeto y al hacerlo puede crear otros objetos. Cuando un objeto deja de usarse en un programa porque ya no se hace referencia a él, entonces el espacio de memoria apartado para sus variables queda disponible para que el controlador de memoria RAM de Java lo utilice nuevamente. En Java el programador no tiene que preocuparse de destruir los objetos para liberar memoria (aunque si lo desea puede intervenir acelerando el proceso de recuperación de la memoria liberada usando System.gc() que ejecuta el recolector de basura).

Para hacer referencia a las variables de un objeto, se escribe el nombre del objeto, un punto y el nombre de la variable correspondiente. Por ejemplo:

p.X=7;

asigna el valor 7 a la variable X del objeto p.


Métodos.


Los métodos de un objeto son funciones que pueden operar sobre las variables del objeto.

Para hacer referencia a un método de un objeto, se escribe el nombre del objeto, un punto y el nombre del método agregando entre paréntesis los parámetros del método o bien unos paréntesis sin nada dentro si el método no acepta parámetros.

Por ejemplo, si p es un punto, entonces para llamar al método que lo mueve 3 unidades a la derecha, basta escribir:

p.moveRight(3);

Éste es el código de un pequeño programa que utiliza la clase punto. El programa crea un punto llamado p con coordenadas (10,10). Luego escribe el valor de la abscisa, llama al método moveLeft de p con parámetro 5, con lo cual desplaza al punto cinco unidades a la izquierda, y escribe el nuevo valor de la abscisa de p.

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

Como ejercicio el alumno debe ir al DOS, compilar punto01.java y ejecutarlo. Podrá comprobar por ejemplo que si antes de la llamada a p.moveLeft(5) la abscisa vale 8, después vale 3.

Observe cómo se usa el constructor para crear un objeto p, es decir una instancia de la clase, que es lo que utiliza en el programa. Observe que el programa no manipula directamente las variables de p sino que lo hace a través del método moveLeft.

Una observación que no tiene que ver con el contenido de esta lección pero que viene al caso por el uso que se ha hecho en este ejemplo es la siguiente: la función System.out.println permite que se le pase un parámetro "mixto" de una cadena (String) y un número unidos por un + que actúa concatenando la primera cadena con la que resultaría de convertir el número a una cadena. Este funcionamiento "inteligente" de println es muy útil pero es atípico de Java. El alumno no debe esperar que otros métodos funcionen así. En general estas conversiones deberá hacerlas el programador explícitamente.


Herencia y extensión de clases (subclases).


Además de contener las variables y los métodos, las clases tienen otra función muy importante en la programación orientada a objetos: la herencia.

Una clase puede ser subclase o extensión de otra. En tal caso se dice que hereda las variables y los métodos de la otra.

Para aclarar este concepto crearemos una subclase de punto llamada circulo.

Si su navegador reconociese la etiqueta de applet, aquí vería un applet

La nueva clase circulo tiene una nueva variable R que interpretaremos como el radio. Pero hereda las variables X e Y de la clase punto. Interpretaremos las variables heredadas como las coordenadas del centro del círculo. La clase circulo también hereda los métodos de punto con los cuales se determina el "funcionamiento" del centro, pero tiene unos métodos nuevos para fijar el valor del radio, para informar dicho valor y para aumentarlo y disminuirlo.

El hecho de que circulo sea una subclase de punto se determina en la línea que define la nueva clase indicando que "extiende" a punto:

public class circulo extends punto

Cuando una clase B es extensión de otra clase A, se dice que B es subclase de A y que A es la superclase de B.

Como se verá en el ejemplo de la siguiente sección, al utilizar la clase circulo no se hace ninguna distinción entre las variables y métodos propios y los heredados de la superclase punto.


Análisis de un applet.


Ahora presentamos un ejemplo de un applet en el que se utiliza la clase circulo.

Este applet utiliza la clase circulo y permite al usuario controlar un objeto de esa clase:
Si su navegador reconociese la etiqueta de applet, aquí vería un applet

Estudie el código de circApplet. Encuentre las líneas donde se crea el objeto c y donde se hacen llamadas a los métodos del objeto.

No se preocupe si no entiende algunos detalles del ejemplo. Es natural pues aún no se ha explicado todo lo que se utiliza en él. Ahora explicaremos todas las partes del applet. En los próximos capítulos se estudiará detalladamente cada uno de los conceptos que aquí sólo se mencionan de manera introductoria.

Es importante que el alumno preste atención a esta explicación para adquirir una visión global de lo que estudiará en las siguientes lecciones. A lo largo de la explicación se introducen por primera vez los conceptos más importantes de la programación en Java y en particular los de la programación de un applet.

La clase circApplet es un Applet, es decir, es una subclase de la clase Applet. Esto se especifica en la línea donde se define circApplet que dice:

public class circApplet extends Applet implements ActionListener

En esta línea se está diciendo (además de otras cosas que se explican más adelante) que la clase que estamos construyendo o definiendo es una extensión de la clase Applet. Esto quiere decir que circApplet posee todas las variables y todos los métodos de la clase Applet, además de los que a continuación se definen explícitamente. Pero que contenga las variables y métodos de la clase Applet significa mucho más de lo que podría parecer en un principio. La clase Applet por definición es una extensión de la clase Panel, que es a su vez extensión de Container, que es extensión de Component que es extensión de Object. Object es la clase básica de la que todas las demás son extensión. Cuando se define una clase y no se dice que es extensión de alguna otra, como se hizo en el caso de circulo, de todas formas es extensión de Object. Por tal motivo, un applet tiene todos los métodos y variables de todas las clases de las que se deriva directa o indirectamente.

A continuación de la declaración de que circApplet es una extensión (o subclase) de Applet, se definen dos variables, una de tipo circulo que se llama c, y otra que es una matriz de 6 botones y se llama b.

Luego aparecen seis métodos cuya función es ofrecer al usuario las operaciones concretas de mover el círculo 10 unidades en cada dirección y aumentar o disminuir el radio en 5 unidades. Hay otro método llamado redraw que se encarga de ofrecer al usuario la opción de dibujar el círculo después de cada movimiento (para entender cómo funciona redraw debe leer la última parte de esta lección donde se explica el método paint). Estos siete métodos se declaran como públicos para que se puedan usar desde otro programa. En particular se usarán desde la página donde se insertará el applet y se usarán a través de JavaScript. En los ejercicios se aprovecha el hecho de que las funciones son públicas. Dentro del applet se usan también las funciones pero para ello no haría falta que fuesen públicas.

A continuación aparece el método

public void init()

Éste es uno de los métodos de la clase Applet. Cuando un navegador abre un applet lo primero que hace es ejecutar el método init() y por tanto es allí donde se debe realizar la construcción del applet. El hecho de definir otra vez este método significa que deseamos "sobreescribirlo", es decir, deseamos modificar su contenido. El contenido original de init() es vacío, por lo tanto si no lo sobreescribimos, no hace nada.

En nuestra implementación de init() creamos los seis botones que servirán para que el usuario pueda mover el círculo. Para crear los botones usamos el constructor de la clase Button y aprovechamos a darle un título a cada botón que nos indique lo que ocurrirá al pulsarlo.

Después aparece el método

public void start()

Éste es otro de los métodos den la clase Applet y el navegador lo ejecuta automáticamente cada vez que se carga el applet y cuando ya está definido su tamaño. Este hecho es muy importante porque en el método init no pueden saberse todavía el ancho y alto del applet. En el ejemplo sobreescribimos el método start para crear en él el objeto circulo que va a utilizarse en el programa.   Al crear el círculo c usamos el constructor de la clase y allí decidimos que su centro tenga inicialmente las coordenadas del centro del panel en donde se dibuja y que su radio sea 40.

La llamada a un constructor siempre se hace poniendo el nombre de una variable de la clase que se va a crear, un signo = y la palabra "new" precediendo al constructor con sus parámetros. La palabra "new" nos indica que se creará un objeto. Esta creación va acompañada de un reclamo de memoria del sistema para acomodar las variables del objeto. Sin embargo el programador de Java no tiene que preocuparse de pedir al sistema esa cantidad de memoria ni de liberarla cuando ya no la utilice. Ambas operaciones las realiza el intérprete de Java automáticamente.

Las últimas seis líneas del método init() realizan las siguientes operaciones:
- Se escoge que el color del fondo del applet sea negro;
- Se escoge que la manera de acomodar los componentes en el applet será con el despliegue "BorderLayout" (ver lección 4);
- Luego se crea un Panel auxiliar que se coloca al "Sur" del applet aprovechando el "BorderLayout"
- En el panel auxiliar se colocan los seis botones tras asignarle al Panel un modelo para acomodar las componentes del tipo "GridLayout" con una fila y seis columnas y se les agrega a los botones el applet como "escucha".

(El tema de los despliegues o "Layouts" se explica detalladamente en la lección 4 y el tema de los escuchas o "Listeners" en la lección 5).

Como hemos dicho anteriormente, los métodos init() y start() son dos de los métodos predefinidos de la clase Applet y nosotros lo hemos sobreescrito haciendo que se encargue de crear el círculo y los botones para moverlo y de acomodar esos botones de alguna manera. Como ya dijimos, init() es el primer método que se llama cuando el navegador crea un applet.  init() sólo se llama una vez en la vida del applet y por ello conviene utilizarlo para construir el applet. En la lección 6 estudiaremos todos los métodos de Applet.

En nuestro circApplet hay dos métodos más que explicar: actionPerformed y paint.

public boolean actionPerformed(ActionEvent ev)

es un método de la interfaz ActionListener que nuestro Applet se ha comprometido a implementar en la última parte de la línea de definición de la clase:

public class circApplet extends Applet implements ActionListener

La interfaz ActionListener consta de un solo método:

public void actionActionListener (ActionEvent ev)

Este método se ejecuta cada vez que se produce un evento del tipo ActionEvent, en particular, cada vez que se pulsa un botón, siempre y cuando el botón haya sido agregado a la lista de "escuchas" de ActionEvent del applet, lo cual se realiza en la línea del método init() que dice:

b[i].addActionListener(this);

El control de eventos es un tema muy importante que se estudia detalladamente en la lección 5. Por ahora será suficiente explicar que cuando se pulsa un botón se llama al método actionPerformed pasándole en el parámetros ev información necesaria para interpretar el evento y actuar. El parámetro ActionEvent ev lleva la información de a cual componente (en este caso un botón) se le hizo un click y dentro del método actionPerformed se determina la acción que se va a realizar según el botón pulsado. Para ello se hace un recorrido por todos los botones usando un "for" (esta instrucción y otras que se usan para crear ciclos en la programación, se estudian en la lección 3). Para separar los seis posibles casos se utiliza un "switch" que es la manera que en Java se hace una disyunción condicional de opción múltiple. Los "switches" y las condicionales binarias, que se hacen con "if" y "else", se estudian también en la lección 3.

Finalmente explicaremos el método

public void paint(Graphics g)

que es un método heredado de la clase Component. El método paint por defecto es vacío. Hay tres métodos de Component que se encargan de "pintar" en varios pasos las componentes y en particular los applets. Estos son repaint(), update(Graphics g) y paint(Graphics g). El primero es el que se llama cada vez que el sistema decide que se tiene que pintar la componente o parte de ella. repaint() obtiene el ambiente gráfico g de la componente, que es un objeto de la clase Graphics (que se estudiará detalladamente en la lección 4) y llama a update(g). update(g) pinta la componente con el color de fondo y llama a paint(g) que por defecto es vacío. Pero el nuevo método paint sí hace algo pues lo hemos sobreescrito, de hecho dibuja el círculo utilizando la función adecuada de la clase Graphics que se llama fillOval y sirve para dibujar círculos y elipses rellenas. A fillOval se le deben pasar como parámetros las coordenadas del punto superior izquierdo del mínimo rectángulo que contiene al "óvalo" y el ancho y alto de dicho rectángulo. Tal vez el alumno piense que sería más cómodo pasarle las coordenadas del centro y los semiejes de la elipse, pero por razones de eficiencia ésta es la función que nos ofrece la clase Graphics.


Índice

Ejercicios de la Lección 02.


José Luis Abreu y Marta Oliveró