Las secciones anteriores introducen ya algunos conceptos "de orientación a objetos". Sin embargo, éstos fueron aplicados en un ambiente procedimental o de una manera verbal. En esta sección investigamos estos conceptos en más detalle y les damos nombres tal y como se usan en los lenguajes de programación orientada a objetos existentes.
La última sección introduce los tipos de datos abstractos (TDAs) como una perspectiva abstracta para definir propiedades de un conjunto de entidades. Los lenguajes de programación orientada a objetos deben permitir la implementación de estos tipos. Consecuentemente, una vez que un TDA se ha implementado, tenemos disponible una representación particular de éste.
Considera nuevamente el TDA Integer. Lenguajes de programación tales como Pascal, C, Modula-2 y otros, ofrecen ya una implementación de dicho tipo. Algunas veces se le llama int o integer. Una vez que has creado una variable de este tipo, ya puedes usar sus operaciones provistas. Por ejemplo, tú puedes sumar dos integers :
int i, j, k; /* Define tres integers */ i = 1; /* Asigna 1 al integer i */ j = 2; /* Asigna 2 al integer j */ k = i + j; /* Asigna la suma de (i) y (j) a (k) */
Juguemos un poco con el fragmento de código de arriba y bosquejemos la relación con el TDA Integer. La primera línea define tres instancias i, j y k de tipo Integer. Consecuentemente, para cada instancia, la operación especial constructor debe ser llamada. En nuestro ejemplo, esto se hace internamente por el compilador. El compilador reserva memoria para almacenar el valor de un integer y le "ata" el nombre correspondiente. Si haces referencia a i, realmente te refieres al área de memoria que se "construyó" por la definición de i. Opcionalmente, los compiladores pueden escoger inicializar la memoria, por ejemplo, podrían darle el valor de 0 (cero).
La línea siguiente
i = 1;
le da a i el valor de 1. Por lo tanto, podemos describir esta línea con ayuda de la notación del TDA del siguiente modo :
Ejecuta la operación set (poner, establecer) con argumento 1 sobre la instacia de Integer i. Esto se escribe como sigue : i.set(1).
Tenemos ahora una representación en dos niveles. El primer nivel es el nivel del TDA, donde expresamos todo lo que se hace a una instancia de este TDA por medio de la invocación de operaciones definidas. En este nivel, pre- y postcondiciones son usadas para describir lo que sucede realmente. En el siguiente ejemplo, estas condiciones se encierran entre llaves.
{ Precondición: i = n donde n es cualquier Integer }
i.set(1)
{ Postcondición: i = 1 }
¡No olvidar que en este momento hablamos acerca del nivel del TDA ! Por consecuencia, las condiciones son condiciones matemáticas.
El segundo nivel es el nivel de la implementación, donde una representation real es seleccionada para la operación. En C, el signo igual "=" implementa la operación set(). Sin embargo, en Pascal se escogió la siguiente representación :
i := 1;
En cualquiera de los dos casos, se implementa la operación set del TDA.
Hagamos un poquito más de hincapié en estos niveles y veamos la línea
k = i + j;
Obviamente, "+" se escogió para implementar la operación add (sumar). Podríamos leer la parte "i + j" como "suma el valor de j al valor de i'', asi, al nivel de TDA, esto resulta en
{ Precondición: Sea i = n1 y j = n2 con n1,
n2 Integers particulares }
i.add(j)
{ Postcondición: i = n1 y j = n2 }
La postcondición asegura que i y j no cambien sus valores.
Por favor recuerda la especificación de add. Dice que un nuevo Integer
es creado con el valor de la suma. Consecuentemente, debemos proveer un mecanismo de acceso a esta nueva instancia. Hacemos esto con la operación set aplicada sobre la instancia k:
{ Precondición: Sea k = n donde n es cualquier Integer }
k.set(i.add(j))
{ Postcondición: k = i + j }
Como podrás notar, algunos lenguajes de programación escogen una representación que es casi igual a la formulación matemática usada en las pre- y postcondiciones. Esto hace algunas veces que sea difícil no confundir ambos niveles.
class Integer { attributes: int i methods: setValue(int n) Integer addValue(Integer j) }
En el ejemplo de arriba, así como en los ejemplos siguientes, usamos una notación que no pertenece a ningún lenguaje de programación específicamente. En esta notación class {...} denota la definición de una clase. En medio de las llaves hay dos secciones, atributos: y métodos: que definen la implementación de la estrutuctura de datos y operaciones del TDA correspondiente. Nuevamente, distinguimos los dos niveles con diferentes términos : Al nivel de implementación hablamos de "atributos", que son elementos de la estructura de datos al nivel del TDA. Lo mismo se aplica a "métodos", que es la implementación de las operaciones del TDA.
En nuestro ejemplo, la estructura de datos consiste de solamente un elemento : una secuencia de dígitos con signo. El atributo correspondiente es un integer ordinario de un lenguaje de programación . Nosotros solamente definimos dos métodos setValue() y addValue() representando las dos operaciones set and add.
Definición (Clase) Una clase es la implementación de un tipo de datos abstracto (TDA). Define atributos y métodos que implementan la estructura de datos y operaciones
del TDA, respectivamente.
Los objetos son identificables en forma única por medio de un nombre. Por lo tanto, tu podrías tener dos objetos distintos con el mismo conjunto de valores. Esto es similar a los lenguajes de programación "tradicional", donde tu podrías tener digamos dos integers i y j ambos equivalentes a "2". Nótese por favor el uso de "i" y "j" en la última oración para darle nombre a los dos integers. Nos referimos al conjunto de valores en un momento en particular como el estatus del objeto.
Definición (Objeto) Un objeto es una instancia de una clase. Puede ser identificado en forma única por su nombre y define un
estatus, el cuál es representado por los valores de sus atributos en un momento en particular.
El estatus de un objeto cambia de acuerdo a los métodos que le son aplicados. Nos referimos a esta posible secuencia de cambios de estatus como el comportamiento del objeto :
Definición (Comportamiento) El comportamiento
de un objeto es definido por un conjunto de métodos que le pueden ser aplicados.
Tenemos presentados ahora dos conceptos principales de orientación a objetos, clase y objeto. La programación orientada a objetos es por lo tanto la implementación de tipos de datos abstractos o, en palabras más sencillas, la escritura de clases. En "runtime", instancias de estas clases -los objetos- cumplen con el objetivo del programa cambiando sus estatus. Por consecuencia, tu puedes pensar que la corrida de tu programa constituye una colección de objetos. Se genera la pregunta : ¿ Cómo interactúan estos objetos? De modo que introducimos el concepto de mensaje en la sección siguiente.
Integer i; /* Definir un nuevo objeto integer */ i.setValue(1); /* Ponerle el valor 1 */
para expresar el hecho de que el objeto integer i debería poner su valor a 1. Este es el mensaje "Aplica el método setValue con argumento 1 sobre tí mismo." enviado al objeto i. Nosotros usamos la notación "." para el envío de un mensaje. Esta notación se usa también en C++ ; otros lenguajes orientados a objetos pueden usar otras notaciones, por ejemplo " -".
Mandar un mensaje pidiéndole a un objeto que aplique un método es similar a una llamada a un procedimiento en lenguajes de programación "tradicionales". Sin embargo, en orientación a objetos, hay un cuadro de objetos autónomos que se comunican unos con los otros por medio de el intercambio de mensajes. Los objetos reaccionan cuando reciben mensajes por medio de la aplicación de métodos sobre si mismos. También pueden negar la ejecución de un método, por ejemplo si el objeto que hace la llamada no tiene permiso para ejecutar el método solicitado.
En nuestro ejemplo, el mensaje y el método que debía ser aplicado una vez que el mensaje se hubiera recibido, tienen el mismo nombre : Nosotros mandamos "setValue con argumento 1" al objeto i el cuál aplica "setValue(1)".
Definición (Mensaje) Un mensaje es una solicitud a un objeto para invocar uno de sus métodos. Un mensaje por lo tanto contiene
Por consecuencia, la invocación de un método es solamente una reacción causada por el hecho de recibir un mensaje. Esto solamente es posible si el método es realmente conocido por el objeto.
Definición (Método)
Un método está asociado a una clase. Un objeto invoca un método como una reacción al recibir un mensaje.
Ver un programa como una colección de objetos interactuantes es un principio fundamental en la programación orientada a objetos. Los objetos en esta colección reaccionan al recibir mensajes, cambiando su estatus de acuerdo a la invocación de métodos que a su vez pdrían causar que otros mensajes fueran enviados a otros objetos. Esto se ilustra en la Figura 4.1.
En esta figura, el programa consiste en solamente cuatro objetos. Estos objetos se mandan mensajes unos a otros, como se indica por las flechas. Nótese que el tercer objeto se manda un mensaje a sí mismo.
¿Cómo nos ayuda esto en el desarrollo de software ? Para responder a esta pregunta, recordemos como hemos desarrollado software con lenguajes de programación procedimentales. El primer paso era dividir el problema en fragmentos manejables más pequeños. Típicamente, estos fragmentos estaban orientados a los procedimientos que tomaban lugar para resolver el problema, más que a los datos involucrados.
Como ejemplo, considera tu computadora. Especialmente, como un caracter aparece en la pantalla cuando oprimes una tecla. En un ambiente procedimental, tu escribes los varios pasos necesarios necesarios para desplegar un carácter en la pantalla :
Tu no distingues entidades con propiedades bien definidas y comportamiento bien definido. En un ambiente orientado a objetos, tu harías distinción entre los objetos interactuantes tecla y pantalla. Una vez que una tecla recibe un mensaje de cambiar su estatus a "estar presionada", su objeto correspondiente manda un mensaje al objeto que representa a la pantalla. Este mensaje le solicita al objeto pantalla que despliegue el valor asociado a la tecla presionada.