Lp13

49
1 Lenguajes de programación Abdiel E. Cáceres González Instituto Tecnológico de Monterrey Campus Ciudad de México Verano 2004 Tower of Babel by Gustav Dore 1986

Transcript of Lp13

Page 1: Lp13

1

Lenguajesde

programaciónAbdiel E. Cáceres GonzálezInstituto Tecnológico de Monterrey

Campus Ciudad de MéxicoVerano 2004

Tower of Babel by Gustav Dore 1986

Page 2: Lp13

2

Métodos

Un método de una clase define las operaciones que una instancia de la clase ejecutará cuando se reciba un mensaje correspondiente a ese método. En cierto sentido, los métodos son como las definiciones de funciones, incluyendo el uso de parámetros y la capacidad de regresar valores.

Page 3: Lp13

3

Métodos

La forma sintáctica general de un método en Smalltalk es:

patrón_del_mensaje[ |variables temporales| ] sentencias

donde los corchetes son meta-símbolos que indican que lo que se encuentra dentro de ellos es opcional.

Page 4: Lp13

4

Métodos

Dado que Smalltalk no tiene declaraciones de tipo, las variables temporales, cuando están presentes, sólo necesitan ser nombradas en una lista. Las variables temporales existen sólo durante la ejecución del método en el cual están listadas. No hay signos de puntuación al final de un método.

El mensaje del patrón corresponde a las sentencias procedurales de lenguajes imperativos tales como Pascal. Los patrones de mensajes, los cuales son prototipos para los mensajes, pueden estar en una de dos formas básicas.

Page 5: Lp13

5

Métodos

Para los mensajes unarios o binarios, sólo se incluye el nombre del método. Para los mensajes de palabra clave, se incluyen las palabras clave y los nombres de los parámetros del patrón de mensaje.

Para indicar que un método regresará un valor, se utiliza la flecha hacia arriba (^). En muchos casos, esta es la última expresión que aparece en el método. Si no se especifica un valor de retorno en un método, el objeto receptor mismo es el valor de retorno.

Page 6: Lp13

6

Clases

Todos los objetos de Smalltalk son instancias de clases. Una clase tiene cuatro partes:

1) Un nombre (de la clase).

2) El nombre de la superclase, el cual especifica la posición de la nueva clase en la jerarquía de clases del sistema.

3) Una declaración de las variables locales, llamadas variables instanciadas. Estas declaraciones estarán disponibles a las instancias de la clase.

4) Los métodos que definen cómo responderán las instancias de la clase a los mensajes. (Recordemos que un objeto hereda también el método de todas las clases que son sus ancestros).

Page 7: Lp13

7

Clases

Los mensajes a un objeto normalmente hacen que se busque un método correspondiente en la clase a la que el objeto pertenece. Si la búsqueda falla, se continúa ésta en la superclase de esa clase y así sucesivamente, hasta llegar a la clase del sistema, llamada “Object”, la cual no tiene superclase.

Si no se encuentra un método en ninguna parte de esa cadena, entonces ocurre un error. Es importante recordar que este método de búsqueda es dinámico.

Page 8: Lp13

8

Clases

Ejemplo de una definición de clase:"Smalltalk Example Program"

"The following is a class definition, instantiations

of which can draw equilateral polygons of any

number of sides"

class name Polygon

superclass Object

instance variable names ourPen

numSides

sideLength

"Class methods"

"Create an instance"

new

^ super new getPen

Page 9: Lp13

9

Clases

"Get a pen for drawing polygons"

getPen

ourPen ← Pen new

"Instance methods"

"Draw a polygon"

draw

numSides timesRepeat: [ourPen go: sideLength; turn: 360 // numSides]

"Set length of sides"

length: len

sideLength len

"Set number of sides"

sides: num

numSides num

Page 10: Lp13

10

Clases

Smalltalk sigue el Principio de Abstracción al agrupar los objetos que tienen comportamiento y propiedades similares bajo la misma clase. Nótese que simultáneamente, los particulares que distinguen un objeto de otro se omiten.

Este enfoque resulta ideal para simulación y es una muestra de la fuerte influencia de Simula-67 en Smalltalk.

La memoria privada de una instancia de una clase contiene sus variables instanciadas.

Las variables instanciadas no son visibles a otros objetos. Cada variable instanciada se refiere a un objeto, al que se denomina “valor”. Los valores de todas las instancias de una cierta variable representan (en su conjunto) el estado actual de esa instancia.

Page 11: Lp13

11

Clases

Las variables instanciadas pueden ser “nombradas” o “indizadas”.

Las variables nombradas corresponden a apuntadores a tipos distintos de los arreglos en un lenguaje imperativo.

Las variables indizadas no se accesan mediante su nombre, sino mediante los mensajes que usan parámetros enteros.

La mayor parte de las variables indizadas se usan de tal forma que corresponden a arreglos en lenguajes imperativos convencionales, aunque la indización misma se realiza a través del paso de mensajes.

Page 12: Lp13

12

Clases

El parámetro entero en un mensaje se usa para referenciar una variable indizada instanciada y corresponde al subíndice de un arreglo en un lenguaje imperativo convencional.

Las instancias de una clase se crean enviando el mensaje “new” a la clase en una asignación, la cual hace que la variable a su izquierda haga referencia al nuevo objeto recién creado.

Por ejemplo:

ourPen ← Pen new

crea una instancia de la clase “Pen” (enviando el mensaje “new” a la clase “Pen”).

Page 13: Lp13

13

Clases

Esto también hace que la variable “ourPen” haga referencia a esta nueva instancia de la clase “Pen”.

Este ejemplo demuestra que pueden enviarse mensajes tanto a las clases como a los objetos.

Las variables de Smalltalk no tienen tipo; cualquier nombre puede asociarse con cualquier objeto.

El único chequeo de tipos que se efectúa ocurre dinámicamente cuando un mensaje se envía a un objeto.

Page 14: Lp13

14

Chequeo de Tipos

Si el objeto es una de sus clases previas (ancestro), entonces tiene un método para el mensaje, el mensaje es legal y el objeto reacciona a él. Si no hay método que corresponda a un mensaje, ya sea en el objeto en una de sus clases ancestrales, entonces se produce un error en tiempo de ejecución.

Este es un concepto de chequeo de tipos significativamente diferente del utilizado en los lenguajes imperativos. El chequeo de tipos en Smalltalk tiene como objetivo simplemente asegurar que un mensaje corresponde a un cierto método.

Advierta que el chequeo dinámico de tipos no implica en este caso un chequeo débil de tipos. Smalltalk tiene un chequeo fuerte de tipos, a pesar de que éste es dinámico (en vez del chequeo estático de lenguajes como Pascal y Ada).

Page 15: Lp13

15

Chequeo de Tipos

Una ventaja del chequeo dinámico de tipos es su flexibilidad, ya que podemos hacer el mismo trabajo que con los paquetes genéricos de Ada, pero sin recurrir a la inherente complejidad de esa técnica.

Puesto que Smalltalk es un lenguaje interpretado y no compilado, el activar mensajes en tiempo de ejecución es algo aceptable y la motivación para detectar tan temprano como sea posible un error que se aplica a los lenguajes compilados no tiene razón de ser en este caso.

Asimismo, no hay violaciones al Principio de Seguridad, porque Smalltalk sigue checando si un objeto tiene un método que responda a un cierto mensaje y, de no encontrarlo, activa un error. De tal forma, se impide que el programa se colapse debido a violaciones al sistema de tipos.

Page 16: Lp13

16

Herencia

La herencia es naturalmente un concepto central en Smalltalk. El sistema de Smalltalk incluye una enorme jerarquía de clases. La programación en Smalltalk consiste, en gran medida en crear subclases de las clases existentes.

Es precisamente este reuso potencial de código existente lo que resulta más interesante y valioso sobre el mecanismo de herencia. De hecho, la herencia es quizás la contribución más importante de los lenguajes orientados a objetos.

Page 17: Lp13

17

Herencia

Las subclases pueden construirse basadas en el comportamiento de sus superclases, pero también pueden modificarlo. Recordemos que en los lenguajes estructurados, una redeclaración de una variable anula la declaración previa.

Lo mismo ocurre en Smalltalk con los métodos: la definición de un método en una subclase anula cualquier definición que pueda existir de ese mismo método en sus superclases.

Un objeto puede enviarse un mensaje a sí mismo porque es un miembro de su superclase. Como puede verse, Smalltalk es un sistema muy extensible, en el cual el reuso de software tiene un significado real.

Page 18: Lp13

18

Herencia

Un problema con las jerarquías definidas en Smalltalk es que su organización impide el uso de una clasificación ortogonal. En la vida real, frecuentemente nos encontramos con objetos que deben ser clasificados de varias formas diferentes.

Por ejemplo, un biólogo podría clasificar a los mamíferos como primates, roedores, rumiantes, etc. Alguien interesado en los usos de los mamíferos podría clasificarlos como mascotas, bestias de carga, fuentes de comida, etc. Finalmente, un zoológico podría clasificarlos como Norteamericanos, Sudamericanos, Africanos, etc.

Page 19: Lp13

19

Herencia

Estas son 3 clasificaciones ortogonales: cada una de las clases corta a través de las otras en ángulo recto. En Smalltalk, el programador está forzado a violar ya sea el Principio de Seguridad o el de Abstracción, a fin de modelar una clasificación ortogonal.

Es decir, tenemos que repetir atributos de algunos objetos en otras clases (violando el Principio de Abstracción) o tenemos que efectuar una clasificación anidada de clases (violando el Principio de Seguridad). La fuente del problema es que un objeto puede pertenecer a sólo una clase a la vez.

Cuando se define una clase, puede especificarse que será una subclase inmediata de exactamente una clase (y sólo una). Una solución posible a este problema que sigue siendo tema de investigación es permitir la herencia múltiple.

Page 20: Lp13

20

Implementación: Clases y Objetos

Una de las cosas más interesantes de Smalltalk es que la mayor parte de su sistema está escrito en Smalltalk.

Esto incluye el compilador, el de-compilador, el depurador, los editores y el sistema de archivos. En total, esto constituye alrededor del 97% del código de Smalltalk. 97%

3%

Others Smalltalk

Page 21: Lp13

21

Implementación: Clases y Objetos

Una de las razones principales por las cuales Smalltalk puede programarse en Smalltalk es que la mayor parte de las estructuras de datos de la implementación, tales como los registros de activación del lenguaje, son objetos de Smalltalk.

Esto significa que pueden ser manipulados mediante programas en Smalltalk y que tienen las propiedades de los objetos de Smalltalk (por ejemplo, se auto-despliegan).

La parte de Smalltalk que no es portátil es llamada “Máquina Virtual de Smalltalk-80”; su tamaño va de 6 a 12K en ensamblador. Los diseñadores de Smalltalk aseguran que toma alrededor de un año producir una versión completamente depurada de una “Máquina Virtual de Smalltalk”. Esto hace de Smalltalk un excelente ejemplo del Principio de Portabilidad.

Page 22: Lp13

22

Implementación: Clases y Objetos

La Máquina Virtual de Smalltalk tiene 3 componentes principales:

1) Manejador de Almacenamiento: Es el manejador de tipos de datos abstractos para los objetos. Encapsula la representación de los objetos y la organización de la memoria.

Las únicas operaciones que otros módulos pueden realizar sobre los objetos son aquellas proporcionadas por el Manejador de Almacenamiento (Storage Manager):

1) Tomar la clase de un objeto.

2) Tomar y almacenar los campos de los objetos.

3) Crear nuevos objetos.

Page 23: Lp13

23

Implementación: Clases y Objetos

Puesto que el Manejador de Almacenamiento tiene que ser capaz de crear nuevos objetos, debe poder obtener espacio libre donde colocarlos, también. Por lo tanto, otra responsabilidad del Manejador de Almacenamiento es colectar y manejar el espacio libre de memoria.

Para este propósito, usa una estrategia de conteo de referencias con extensiones para poder manejar estructuras cíclicas.

Page 24: Lp13

24

Implementación: Clases y Objetos

2) Intérprete: Es el corazón del sistema de Smalltalk.

Aunque sería posible interpretar directamente las formas escritas en Smalltalk, es más eficiente si el intérprete opera sobre una forma intermedia del programa. El intérprete es esencialmente el manejador de tipos de datos abstractos para los métodos.

Page 25: Lp13

25

Implementación: Clases y Objetos

Subrutinas Primitivas: Es simplemente una colección de los métodos que, por razones de desempeño, se implementan en lenguaje máquina en vez de hacerse en Smalltalk. Aquí se incluyen las funciones básicas de entrada/salida, la aritmética con enteros, el indizamiento de los objetos que lo requieren (p.ej., los arreglos) y las operaciones gráficas básicas.

La representación de un objeto debe contener sólo la información que varía de objeto a objeto. La información que sea igual para una clase de objetos se almacena en la representación de dicha clase.

Page 26: Lp13

26

Representación de Objetos

La información que varía entre las instancias de una clase es simplemente la correspondiente a las variables de instanciación. La información almacenada con la clase incluye los métodos de la clase y los métodos de la instancia.

Puesto que necesitamos saber la clase a la que pertenece cada objeto, la representación de un objeto debe contener alguna indicación de su clase correspondiente.

Hay varias formas de hacer eso: la forma más simple es incluir en la representación del objeto un apuntador a la estructura de datos que representa la clase.

Page 27: Lp13

27

Representación de Objetos

len. 6c.d.loctiltsize

scribe

box

point

500

200

500@600

len. 6c.d.loctiltsize

scribe

len. 4

c.d.

x

y

B1

B2

Page 28: Lp13

28

Representación de Objetos

Además de las variables de instanciación y de la descripción de la clase, cada objeto tiene un campo de longitud requerido por el manejador de almacenamiento para asignar, liberar y mover objetos en memoria.

Puesto que el Smalltalk mantiene la regularidad en todo momento, no debiera sorprendernos que las clases sean también objetos, los cuales son instanciaciones de la metaclase.

Por lo tanto, las clases se representan en la misma forma que los objetos, pero con un apuntador a la metaclase. Las variables instanciadas de una clase contienen apuntadores a los objetos que representan la información que es igual para todas las instancias de la clase.

Page 29: Lp13

29

Representación de la clase Object

len 8c.d.name

superclassinst.vars

class msgs.ist. msgs.inst. size 4

class

“box”

displayObject

“loc tilt size scribe”

message dict.

message dict.

boxclass

string

messageDictionary

class

string

messageDictionary

Page 30: Lp13

30

Representación de Clases

Esta información incluye lo siguiente:

El nombre de la clase

La superclase (la cual es “object” si no se especifica otra)

Los nombres de las variables instanciadas

El diccionario de mensajes instanciados

Advierta que en este caso es necesario conocer el número de variables instanciadas para determinar la cantidad de almacenamiento requerida. Si no tuviésemos este campo, sería más lento efectuar instanciaciones de objetos.

Page 31: Lp13

31

Representación de Clases

Puesto que sería demasiado lento que el intérprete tuviese que decodificar el formato fuente de un método cada vez que el método fuese ejecutado, es una buena idea compilar los métodos en alguna forma de pseudo-código que pueda interpretarse rápidamente.

Por otra parte, el formato fuente de los métodos se requiere para editar y desplegar las definiciones de la clase. Por lo tanto, los diccionarios de mensajes contienen dos entradas para cada formato del mensaje: una que contiene el formato fuente del método y otra que contiene una forma compilada.

Para encontrar estas entradas en el diccionario de mensajes, se utilizan técnicas de “hashing”.

Page 32: Lp13

32

Representación de los Registros de Activación

El paso de mensajes en Smalltalk es muy similar a la invocación de procedimientos en los lenguajes estructurados. Por lo tanto, no debiera sorprendernos que se usen técnicas de implementación muy similares, aunque con algunas diferencias importantes.

Hemos visto que los registros de activación (RAs) son el vehículo principal para la implementación de procedimientos. Puesto que un RA tiene toda la información pertinente a una activación del procedimiento, el proceso de invocar un procedimiento y regresar de él puede entenderse como la manipulación de RAs.

Lo mismo aplica a Smalltalk: usaremos registros de activación para conservar toda la información relevante a una activación de un método.

Page 33: Lp13

33

Representación de los Registros de Activación

La estructura de un RA en Smalltalk es muy similar a la usada en los lenguajes estructurados:

1) Parte del ambiente: El contexto a usarse para la ejecución del método.

2) Parte de la instrucción: La instrucción a ejecutarse cuando este método continúa.

3) Parte del remitente: El registro de activación del método que envió el mensaje invocando este método (esto es similar a la liga dinámica).

Page 34: Lp13

34

Representación de los Registros de Activación

Se usa un sistema de coordenadas en dos dimensiones para identificar instrucciones:

Un apuntador al objeto identifica el método-objeto que contiene todas las instrucciones de un método.

Un desplazamiento relativo que identifica la instrucción en particular dentro del par método-object

Este direccionamiento en coordenadas bidimensionales es necesario porque el direccionamiento de instrucciones pasa a través del manejador de almacenamiento.

Esto último indica que nos adherimos al Principio de Ocultamiento de Información.

La parte del ambiente debe proporcionar acceso tanto al ambiente local como al no local.

Page 35: Lp13

35

Representación de los Registros de Activación

El ambiente local incluye espacio para los parámetros del método y para las variables temporales. Esta parte del registro de activación debe proporcionar espacio para variables temporales ocultas tales como los resultados intermedios de las expresiones.

El ambiente no local incluye a todas las otras variables visibles (es decir, las variables instanciadas y las variables de la clase). Las variables instanciadas se almacenan en la representación del objeto que recibió el mensaje. Por lo tanto, un simple apuntador a este objeto las vuelve accesibles.

Page 36: Lp13

36

Representación de los Registros de Activación

localsparameters

temporary vars.intermediates

non locals static link

instruction partmethod

instructionsender part dynamic link

length

c.d.

instance vars.

lengthc.d.

superclass

class variables

lengthc.d.

superclass

class variables

Activation recordinstance

ClassSuperclass

Page 37: Lp13

37

Representación de los Registros de Activación

La representación del objeto contiene un apuntador a la clase de la cual es una instancia el objeto; por lo tanto, las variables de la clase también son accesibles vía la referencia al objeto. Finalmente, puesto que la representación de la clase también contiene un apuntador a su superclase, las variables de la superclase son también accesibles.

Advierta que el enfoque usado por Smalltalk para acceder ambientes no locales es muy similar a la cadena estática que estudiamos anteriormente. En esa técnica, la cadena estática nos llevaba del ambiente activo más anidado al ambiente activo más exterior.

En el caso de Smalltalk, la cadena estática nos lleva del método activo al objeto que recibió el mensaje, y de ahí hacia arriba, a través de la jerarquía de clases, hasta terminar en la clase “object” (la metaclase).

Page 38: Lp13

38

Representación de los Registros de Activación

Al igual que en los lenguajes estructurados, el acceso a las variables requiere conocer la distancia estática a la variable, de manera que se salte ese número de ligas de la cadena estática para llegar al ambiente que define la variable.

Page 39: Lp13

39

Paso y Regreso de Mensajes

Cuando se envía un mensaje a un objeto, deben efectuarse los siguientes pasos:

1) Crear un registro de activación para el receptor (“callee”, en inglés).

2) Identificar el método que está siendo invocado extrayendo el patrón (template) del mensaje y buscándolo en el diccionario de mensajes de la clase del objeto receptor o en sus superclases.

3) Transmitir los parámetros al registro de activación del receptor.

4) Suspender al remitente (invocador) almacenando su estado en su registro de activación

5) Establecer una ruta (liga dinámica) del receptor al remitente, y establecer como activo el registro de activación del receptor.

Page 40: Lp13

40

Paso y Regreso de Mensajes

primerArreglo at: 1 put: 5

localsparameters

temporary vars.intermediates

non locals static link

instruction partmethod

instructionsender part dynamic link

primerArreglo

1

2

3

4

5

Page 41: Lp13

41

Paso y Regreso de Mensajes

Como es de esperarse, regresar de un método debe revertir este proceso:

1) Transmitir el objeto retornado (si hay alguno) del receptor al remitente

2) Continuar la ejecución del remitente, restaurando su estado a partir de su registro de activación

Advierta que los registros de activación no son liberados de la memoria explícitamente, sino que se usa recolección de basura. El Manejador de Almacenamiento crea el espacio libre y reclama espacio que un objeto ya no está usando de vez en cuando.

Este enfoque es muy diferente del usado en los lenguajes estructurados, en los cuales los registros de activación ocupan posiciones contiguas en una pila. El enfoque de Smalltalk es menos eficiente, pero es más regular y más simple.

La otra razón para no usar una pila tiene que ver con la concurrencia. Las pilas sólo son apropiadas si estamos lidiando con algo que siga una estructura LIFO (Last-In, First-Out).

Page 42: Lp13

42

Paso y Regreso de Mensajes

Los procedimientos en un lenguaje procedural siguen esta estructura, pero los procedimientos en un lenguaje concurrente no. Por lo tanto, necesitamos un modelo diferente para lidiar con las tareas concurrentes en Smalltalk.

Page 43: Lp13

43

Puntos Finales sobre Smalltalk

Smalltalk es un buen ejemplo de lo que puede hacerse con un lenguaje pequeño, simple y extremadamente regular.

Smalltalk ha sido utilizado en un sinnúmero de aplicaciones: juegos, simulaciones, gráficos, inteligencia artificial, etc.

Page 44: Lp13

44

Puntos Finales sobre Smalltalk

Dado que Smalltalk no tiene tipos, hablar de polimorfismo no es muy adecuado, pero sí podemos mencionar que la sobrecarga se da de manera natural. No hay ninguna razón por la que objetos de diferentes clases no puedan responder a un mismo mensaje.

El diseño de Smalltalk ha sido guiado por varios principios muy valiosos: Simplicidad, Regularidad, Abstracción, Seguridad y Ocultamiento de Información. En general, ilustra un principio de MacLennan que no habíamos mencionado: el de la Elegancia.

Page 45: Lp13

45

Extensiones Orientadas a Objetos

Varios lenguajes han sido extendidos para soportar el paradigma de orientación a objetos. Por ejemplo: LISP, C y Ada-83.

Ada 95 posee nuevos mecanismos que hacen más flexible su manejo de clases y tipos. De hecho, se manejan clases como tipos especiales (restringidos) que pueden pasarse como parámetros pero no pueden usarse como tipos de variables.

Sin embargo, el soporte de orientación a objetos en Ada es realmente muy complejo y costoso. Esto se debe a que se pretende agregar un nuevo mecanismo que implica tipos dinámicos sin sacrificar la estructura de un sistema de tipos estáticos.

Page 46: Lp13

46

Extensiones Orientadas a Objetos

C++ es una extensión orientada a objetos del popular lenguaje C. C++ fue desarrollado también en Laboratorios Bell y es, en gran parte, responsabilidad de Bjarne Stroupstrup.

Las clases en C++ son una generalización del los registros (records) de C. C++ soporta objetos representados internamente a través de la declaración de clases. Nótese, sin embargo, que C++ permite herencia múltiple. Esto se debe, en buena medida, a que usa un sistema de tipos estático.

Bjarne Stroupstrup

Page 47: Lp13

47

Extensiones Orientadas a Objetos

Los componentes de una clase en C++ pueden ser de tipo “public” (visibles a todo mundo), “protected” (visibles en la clase y sus subclases) o “private” (visibles sólo dentro de la clase). La programación orientada a objetos de C++ depende en buena medida de las funciones virtuales.

C++ se vale de los “templates” para implementar un mecanismo similar a los paquetes genéricos de Ada.

La mayor crítica al C++ se debe a que retiene las características estáticas (de los tipos, sobre todo) de C a fin de poder compilar ambos lenguajes.

Page 48: Lp13

48

Extensiones Orientadas a Objetos

Java ha sido foco de atención más recientemente y puede verse como una variante del C++. En Java se han enfatizado aspectos de seguridad, robustez, portabilidad y asociaciones tardías. El enfoque principal del lenguaje es la programación en ambientes de red y sistemas distribuidos.

Java omite varias funciones de C++, como por ejemplo sobrecarga de operadores, apuntadores, coerciones excesivas y herencia múltiple (sólo se permite de manera más restringida), pero a cambio agrega otras tales como recolección de basura automática y un chequeo seguro de tipos.

Page 49: Lp13

49

Extensiones Orientadas a Objetos

CLOS (Common LISP Object System) es una extensión bastante popular del LISP que permite que éste soporte orientación a objetos. CLOS proporciona objetos representados externamente dado que los métodos se implementan como funciones genéricas. Dado que LISP tiene tipos dinámicos, éstos se mantienen en la orientación a objetos.

Un detalle interesante de CLOS es que sí soporta herencia múltiple, pero el mecanismo para realizarla dista de lo trivial. Las reglas de la herencia múltiple deben expresarse en la forma de un algoritmo a fin de determinar la lista de precedencia de clases que se requiere.