Programación orientada a objetos Abdiel E. Cáceres González Centro de Investigación y de Estudios Avanzados - IPN México D.F., México. 2004 Objetos, mensajes y encapsulamiento Objective-C es un lenguaje híbrido de programación, comercializado por The Stepstone Corporation (antes Productivity Products International). Se formó injertando el estilo de programación orientada a objetos de Smalltalk-80 en un material básico que era C. Objective-C añade precisamente un nuevo tipo de datos, el objeto, y una operación nueva, la expresión con mensajes. Mejoras en el lenguaje Mecanismo de ligadura en el momento de ejecución Modelos de computación de universo abierto No se necesita conocer la relación entre las partes individuales y el todo Objetos, mensajes y encapsulamiento Un objeto es unos datos y un conjunto de operaciones que pueden acceder a esos datos. Se indica a un objeto que lleve a cabo una de sus operaciones enviándole un mensaje seleccionando primero la operación que realiza el nombre del mensaje, ejecutando esta operación y devolviendo después el control al que hizo la llamada Mensaje al objeto A: realiza la operacion4 operacion8 operacion1 operacion2 operacion3 datos operacion4 operacion5 operacion6 operacion7 Objetos, mensajes y encapsulamiento Un objeto es unos datos y un conjunto de operaciones que pueden acceder a esos datos. Se indica a un objeto que lleve a cabo una de sus operaciones enviándole un mensaje seleccionando primero la operación que realiza el nombre del mensaje, ejecutando esta operación y devolviendo después el control al que hizo la llamada Mensaje al objeto A: realiza la operacion4 operacion8 operacion1 operacion2 operacion3 datos operacion4 operacion5 operacion6 operacion7 Objetos, mensajes y encapsulamiento Un objeto es unos datos y un conjunto de operaciones que pueden acceder a esos datos. Se indica a un objeto que lleve a cabo una de sus operaciones enviándole un mensaje seleccionando primero la operación que realiza el nombre del mensaje, ejecutando esta operación y devolviendo después el control al que hizo la llamada Mensaje al objeto A: realiza la operacion4 operacion8 operacion1 operacion2 operacion3 datos operacion4 operacion5 operacion6 operacion7 Objetos, mensajes y encapsulamiento Un objeto es unos datos y un conjunto de operaciones que pueden acceder a esos datos. Se indica a un objeto que lleve a cabo una de sus operaciones enviándole un mensaje seleccionando primero la operación que realiza el nombre del mensaje, ejecutando esta operación y devolviendo después el control al que hizo la llamada Mensaje al objeto A: realiza la operacion4 Esta forma de hablar refleja el hecho consistente en que los objetos son verdadereamente distintos de los operadores y operandos convencionales. operacion8 operacion1 operacion2 operacion3 datos operacion4 operacion5 operacion6 operacion7 Objetos, mensajes y encapsulamiento Un operando convencional se limita a esperar pasivamente a que le afecte una operación. Pero un objeto son datos activos, porque cada uno contiene un conjunto de operaciones unidas a él indisolublemente en su interior. No es correcto, y ni siquiera es posible, tratar a un objeto en la fotma en que los programadores tratan a los datos, haciéndole algo directamente. Es posible aplicar una función a unos datos, pero no a un objeto. Los datos de un objeto son propiedad privada suyos, y la única forma en que se puede acceder a esos datos es solicitar que lo haga el objeto, ejecutando uno de sus procedimientos cuando se lo solicita. Objetos, mensajes y encapsulamiento solicitar significa ejecutar una sentencia del lenguaje de programación llamasa expresión de mensaje. Un aexpresión de mensaje se parece a una llamada a procedimiento convencional, con un paso adicional: Hay un mecanismo de selección que proporciona la ligadura dinámica. Tanto las funciones como los mensajes llevan argumentos, y cualquiera de los dos puede proporcionar un valor. En ambos, el contexto de la función que hace la llamada se guarda en una pila, y se restaura cuando finaliza el cálculo. En ambos, el que hace la llamada queda bloqueado hasta que concluye el destinatario Objetos, mensajes y encapsulamiento Utilizar la palabra “mensaje” para algo tan sencillo solamente complica las cosas, porque la palabra posee fuertes connotaciones de concurrencia. Los mensajes no son un mecanismo de concurrencia, sino un mecanismo de modularidad. La mensajería crea la encapsulación de datos y procedimientos que se denomina objeto. Los mensajes no tienen nada que ver con la concurrencia. Los mensajes son exactamente iguales que las llamadas a procedimientos en este aspecto; ni mejores ni peores. Objetos, mensajes y encapsulamiento Las funciones y mensajes se diferencian en varios aspectos: Las funciones pueden tener o no tener argumentos los mensajes siempre tienen al menso un argumento que identifica el objeto que va a recibir el mensaje. El nombre de una función identifica a un único codigo ejecutable El selector de un mensaje no lo hace, porque el código que vaya a ser seleccionado también depende del objeto al que fuera enviado el mensaje. Decir que un objeto “sabe hacer algo” significa solamente que, cuando se ejecuta un mensaje recibido por ese objeto, la búsqueda del mecanismo de selección tiene éxito, y desencadena una de las operaciones del objeto. Objetos, mensajes y encapsulamiento Salvo el mecanismo de selección, cada uno de los aspectos de esta terminologia tiene su contrapartida exacta en la programación convencional de operadores y operandos: un bloque de datos un objeto puntero del bloque de datos identidad del objeto aplicar una función a los datos enviar un mensaje al objeto Objetos, mensajes y encapsulamiento los objetos son una herramienta para construir sistemas, sencilla, pero razonablemente eficiente. Permite a los proveedores ofrecer interfaces especificadas limpiamente para los servicios que proporcionan. Objetos, mensajes y encapsulamiento El consumidor tiene una visibilidad perfecta de los procedimientos que ofrece el objeto, y no tiene visibilidad alguna de los datos. Desde el punto de vista del consumidor, un objeto es una cápsula sin custuras, que le ofece un cierto número de servicios, sin que se pueda ver la forma en que están realizados estos servicios. Visibilidad parcial operacion8 operacion1 operacion2 operacion3 datos operacion4 operacion5 operacion6 operacion7 Objetos, mensajes y encapsulamiento El consumidor tiene una visibilidad perfecta de los procedimientos que ofrece el objeto, y no tiene visibilidad alguna de los datos. Desde el punto de vista del consumidor, un objeto es una cápsula sin custuras, que le ofece un cierto número de servicios, sin que se pueda ver la forma en que están realizados estos servicios. Visibilidad total operacion8 operacion1 operacion2 operacion3 datos operacion4 operacion5 operacion6 operacion7 Objective-C Objective-C es una herramienta pra escribir programas que implican objetos y mensajes. Igualmente es una herramienta para expresar el tipo de programas que los programadores de C han estado escribiendo durante años. Es un lenguaje híbrido que contiene todo el lenguaje C, mas partes importantes de Smalltalk-80. Objective-C fue diseñado por Brad J. Cox, cuyo principal propósito fue agregar las características principales de SmallTalk-80 al lenguaje C. Su trabajo está relacionado con los lenguajes orientados a objetos, hizo un ambiente de programacion inspirado en SmallTalk-80. Objective-C Una de las características clave de Objective-C es que siempre es posible soslayar el mecanismo orientado a objetos para acceder directamente a la información privada de un objeto. Esta es una de las mayores debilidades teóricas de los lenguajes híbridos, y una de sus mayores ventajas prácticas. Frecuentemente lo mejor es desarrollar el código de bajo nivel de manera convencional, explotando la ligadura estática para obtener una alta eficiencia a efectos de máquina, y aprovechando la comprobacion estricta de tipos. Por otro lado, lo mejor para el código del usuario final sele ser utilizar objetos. Es importante que estos niveles se acoplen de manera suave y eficiente. En estas areas de transición, es relativamente frecuente utilizar estas estructuras de C para acceder directamente a la memoria de los objetos. Identificadores de objetos Los objetos se identifican mediante un nuevo tipo de datos de Objective-C, denominado id o identificador de objeto. El identificador del objeto proporciona una clave mediante la cual es posible manipular ese objeto en expresiones de mensajes, que son las unicas operaciones legítimas que pueden aplicarse al id de un objeto. La forma en que un id identifica realmente al objeto es un detalle de realización para el cual hay muchas posibles opciones: a) utilizar la dirección física del objeto como identificador. b) utilizar un offset Objective-C da a conocer esta decisión generando una sentencia typedef en cada archivo. Esto define un nuevo tipo, id, en términos de otro tipo que C ya comprende, es decir, un puntero a una estructura. Identificadores de objetos En adelante, los ids se tratan como nuevos tipos legítimos, que se pueden utilizar con tanta libertad como los tipos estándares incorporados a C (char, int, float, etc). int numero; //declara un entero id unObjeto; //declara un objeto Un id consume una cantidad de memoria fija, lo suficiente para identificar a todos los objetos que puedan existir en cualquier momento dado. Identificadores de objetos Este espacio no es el mismo que ocupan los datos privados del objeto en sí. Los identificadores de objetos son números de tamaño fijo, mientras que el espacio que ocupa un objeto varía para cada clase de objeto. id1 Identificadores de objetos Este espacio no es el mismo que ocupan los datos privados del objeto en sí. Los identificadores de objetos son números de tamaño fijo, mientras que el espacio que ocupa un objeto varía para cada clase de objeto. id1 id2 Identificadores de objetos Este espacio no es el mismo que ocupan los datos privados del objeto en sí. Los identificadores de objetos son números de tamaño fijo, mientras que el espacio que ocupa un objeto varía para cada clase de objeto. id1 id2 id3 Expresiones con mensajes Una vez que el usuario tiene el identificador de un objeto, puede acceder a los servicios del objeto, escribiendo una expresión que contenga un mensaje. Un mensaje es una orden, que se le da a un objeto para que haga algo, y mientras tanto el que hace la llamada espera hasta que el receptor lo haya hecho y le proporcione una respuesta. Enviar un mensaje es exactamente igual que llamar a un procedimiento; la única diferencia estriba en que los mensajes implican una ligadura dinámica Expresiones con mensajes En un sistema orientado a objetos, las operaciones las realizan los objetos, no se les hacen a ellos. Las operaciones se solicitan escribiendo una expresión con un mensaje, que especifica algún objeto, y que le dice lo que debe hacer, especificando el nombre de la acció´n que deba llevar a cabo. Este nombre se denomina selector del mensaje. El objeto que recibe el mensaje busca el selector en una tabla de cosas que sabe hacer, para decidir la forma en que va a cumplir esta orden. Expresiones con mensajes Por comodidad a efectos de realización, todos los objetos traducen los selectores a la realización de la misma manera, y el mecanismo de selección está centralizado en una sola función, llamada mensajero. todas las expresiones que contienen mensajes se transforman al compilar en llamadas a la funcion mensajero; sus argumentos especifican el id del receptor en la cual busca la rutina de mensajería para determinar la forma en que esa clase de objetos realiza el selector. Esta tabla se construye cuando se define el objeto, y no suee cambiarse dinámicamente. Identifica al selector de todos los mensajes que este objeto sepa llevar a cabo, y un puntero del procedimiento mkediante el cual este objeto realiza ese mensaje. Este procedimiento, llamado realización o método, es el cuerpo compilado de una funcion de C. Expresiones con mensajes Por comodidad a efectos de realización, todos los objetos traducen los selectores a la realización de la misma manera, y el mecanismo de selección está centralizado en una sola función, llamada mensajero. todas las expresiones que contienen mensajes se transforman al compilar en llamadas a la funcion mensajero; sus argumentos especifican el id del receptor en la cual busca la rutina de mensajería para determinar la forma en que esa clase de objetos realiza el selector. Esta tabla se construye cuando se define el objeto, y no suee cambiarse dinámicamente. Identifica al selector de todos los mensajes que este objeto sepa llevar a cabo, y un puntero del procedimiento mediante el cual este objeto realiza ese mensaje. Este procedimiento, llamado realización o método, es el cuerpo compilado de una funcion de C. Expresiones con mensajes Los mensajes se envían escribiendo expresiones que contienen mensajes, y que traducen directamente a llamadas al mensajero. De hecho, si no fuera porque se necesita un poco de asistencia en el momento de la compilación para definir nuevas clases, se podría prescindir por completo de la sintaxis especial, y se podrían escribir directamente llamadas a la rutina de mensajería. Expresiones con mensajes unarios La sintaxis de mensajería de Objective-C es una adaptación de la sintaxis de Smalltalk-80 modificada para hacerla compatible con el lenguaje C. Las expresiones con mensajes se escriben en la forma de una pareja de corchetes que se abren y cierran, y que rodean al receptor, al selector y a sus argumentos. El tipo de mensaje más sencillo es parecido a las expresiones unarias de Smalltalk-80, que no admiten argumentos. unConjunto = [Set new]; Esta expresión envía el mensaje unario new al objeto identificado por la variable Set de C. La respuesta a este mensaje se almacena en otra variable, unConjunto. Expresiones con mensajes unarios Las expresiones con mensajes están permitidas en cualquier lugar en que sea admisible una llamada a una función en C. C es relativamente generoso en esto, siendo un lenguaje orientado a expresiones que permite llamadas a funciones, y por tanto expresiones con mensajes en cualquier lugar en que se permita una expresión computacional. Por ejemplo, las expresiones con mensajes pueden aparecer como argumentos en las llamadas a funciones: printf(”El tamaño de este conjunto es %d\n”, [un conjunto sixe]); pueden aparecer dentro de expresiones condicionales, y dentro de estructura del tipo bucle: if ([recipiente is Empty])... o bien while (miembro=[sequence next])... Expresiones con mensajes unarios Pueden aparecer incluso dentro de otras expresiones con mensajes: [unConjunto size]; //receptor = una variable [algunaFuncion(algunArgumento) size]; // receptor = llamada a funcion [[Set new] size]; //receptor = expresion con mensaje La mayoría de los programadores de C nunca soñarían con escribir llamadas profundamente anidadas como esta: printf(”(%d,$d)\n”, coordX(origenDe(unRectangulo)), coordY(esquinaDe(unRectangulo))); Pero, sorprendentemente, pronto se acostumbran a hacer lo mismo con expresiones de mensajes: printf(”(%d,%d)\n”, [[unRectangulo origen] x], [[unRectangulo esquina] y]; Expresiones de mensajes con palabras reservadas Los mensajes que admiten argumentos se llaman expresiones con palabras reservadas. Estas expresiones se escriben en la forma (bastante especial) que se habían descrito anteriormente para SmallTalk-80 Mensaje Selector Argumentos [unObjeto hazEsto] hazEsto ninguno [unObjeto hazEsto:arg1] hazEsto: arg1 [ubObjeto do:arg1 with:arg2] do:with: arg1, arg2 Expresiones de mensajes con palabras reservadas Los mensajes que admiten argumentos se llaman expresiones con palabras reservadas. Estas expresiones se escriben en la forma (bastante especial) que se habían descrito anteriormente para SmallTalk-80 Mensaje Selector Argumentos [unObjeto hazEsto] hazEsto ninguno [unObjeto hazEsto:arg1] hazEsto: arg1 [ubObjeto do:arg1 with:arg2] do:with: arg1, arg2 Un selector unitario sin argumentos Expresiones de mensajes con palabras reservadas Los mensajes que admiten argumentos se llaman expresiones con palabras reservadas. Estas expresiones se escriben en la forma (bastante especial) que se habían descrito anteriormente para SmallTalk-80 Mensaje Selector Argumentos [unObjeto hazEsto] hazEsto ninguno [unObjeto hazEsto:arg1] hazEsto: arg1 [ubObjeto do:arg1 with:arg2] do:with: arg1, arg2 Selector de palabras reservadas hazEsto: Expresiones de mensajes con palabras reservadas Los mensajes que admiten argumentos se llaman expresiones con palabras reservadas. Estas expresiones se escriben en la forma (bastante especial) que se habían descrito anteriormente para SmallTalk-80 Mensaje Selector Argumentos [unObjeto hazEsto] hazEsto ninguno [unObjeto hazEsto:arg1] hazEsto: arg1 [ubObjeto do:arg1 with:arg2] do:with: arg1, arg2 Selector de palabras reservadas do:with: Tipos de expresiones con mensajes Las expresiones de mensajes pueden proporcionar valores, así que tienen su tipo, exactamente igual que ocurre con las funciones. Por ejemplo, el mensaje new siempre proporciona un valor del tipo id, mientras que el mensaje size siempre proporciona un valor del tipo int: id unConjunto = [Set new]; int n = [unConjunto size]; El tipo de cualquier mensaje dado queda determinado cuando se define el primer método que tenga ese nombre, y se da a conocer automáticamente a todos los posibles emisores. Ejemplo: Convertidor de monedas Vamos a construir una aplicación de una sola ventana desde el inicio hasta el final para entender la programación en Cocoa. Normalmente el flujo de actividades a realizar es: a) b) c) d) e) g) h) Diseñar la aplicación Crear el proyecto Crear la interface Definir las clases Implementar las clases Construir el proyecto Correr y probar la aplicación Diseñar la aplicación: Convertidor de monedas El modelo MVC (model-view-controller) MVC propone tres tipos de objetos en una aplicación (el modelo, las vistas y el controlador) view controller model Diseñar la aplicación: Convertidor de monedas En los diseños tipo MVC, los objetos en la capa modelo, alojan datos y definen la lógica que manipula esos datos. view controller model Diseñar la aplicación: Convertidor de monedas Los objetos en la capa view representan aquellas características visibles que están en la interface con el usuario (una ventana o un botón por ejemplo) view controller model Diseñar la aplicación: Convertidor de monedas Y los objetos en la capa controller actúan como un mediador entre los objetos de la capa modelo y los objetos de la capa vista. view controller model Diseñar la aplicación: Convertidor de monedas Estrictamente hablando, dependiendo de las circunstancias, al gunas veces es mejor combinar roles entre los objetos. Por ejemplo, en aplicaciones de muchas gráficas, como un juego arcade, en donde quizás existan objetos vista que se mezclen con objetos modelo. view controller model Diseñar la aplicación: Convertidor de monedas El convertidor de modenas consta de 3 objetos - Converter (de la capa modelo), ConverterController (de la capa controller) y CurrencyConverter (de la capa vista). view CurrencyConverter controller ConverterController model Converter Diseñar la aplicación: Convertidor de monedas El objeto CurrencyConverter va a tener algunos elementos visuales (tambien objetos) títulos, botones, campos de texto y una línea. view controller ConverterController model Converter Diseñar la aplicación: Convertidor de monedas El objeto Converter es responsable de calcular el cambio de moneda considerando la cantidad de dinero introducido y devolver ese valor. view CurrencyConverter controller ConverterController model Converter Diseñar la aplicación: Convertidor de monedas El objeto ConverterController coordina la actividad entre el objeto converter y los objetos de la interfaz del usuario UI. view CurrencyConverter controller ConverterController model Converter Diseñar la aplicación: Convertidor de monedas La clase ConverterController asume el rol central en la aplicación. Como todos los objetos de la capa controller, ConverterController se comunica con los objetos de la interface y con los objetos del modelo, y manipula tareas específicas a la aplicación. ConverterController obtiene el valor que el usuario introduce en los campos, luego pasa esos valores al objeto Converter, y pone este resultado en un campo en la interface. view CurrencyConverter controller ConverterController model Converter Diseñar la aplicación: Convertidor de monedas La clase Converter simplemente calcula un valor a partir de dos argumentos pasados por el controller y devuelve el resultado. La idea es que Converter se vuelva reutilizable para otras aplicaciones. view CurrencyConverter controller ConverterController model Converter Creando la interface del convertidor de monedas El primer paso es crear un proyecto en ProjectBuilder: a) Iniciar el programa Project Builder b) Elegir New Project del File menu c) En el panel New Project, seleccione el tipo de aplicación Cocoa Application y haga “clic” en el botón Next d) Nombre la aplicación Currency Converter e) Si lo desea, elija una nueva ubicación f) Termine con Finish Creando la interface del convertidor de monedas Desde el Interface builder se crea una ventana Creando la interface del convertidor de monedas Se redimensiona la ventana a un tamaño adecuado utilizando el inspector Creando la interface del convertidor de monedas Se le ponen los objetos visuales necesarios Definiendo las clases de Currency Converter Primero, las clases proporcionan una taxonomía de objetos, una manera útil de categorizarlos. Segundo, se usan las clases para generar instancias u objetos. Las clases definen las estructuras de datos y el comportamiento de sus intancias, y en el tiempo de ejecución crean e inicializan esas instancias. En un sentido, una clase es como una fábrica, creando objetos a medida que se necesiten. Lo esencialmente diferente de las clases y sus intancias son los datos. Una instancia tiene su propio conjunto de datos, los datos de las clases tambien son datos de todas las instancias de esa clase. De manera estricta, las clases definen la estructura de los datos y sus intancias alojan los datos. Definiendo las clases de Currency Converter En Objective-C todos los objetos pertenecen a subclases de NSObject. NSObject ConverterController view CurrencyConverter controller ConverterController model Converter Las vías de comunicación de los objetos Los OUTLETS y ACTIONS establecen las vías de comunicación de los objetos. Los outlets identifican objetos y las acciones hacen que los objetos reciban ordenes de otros objetos, como los botones. ConverterController Las vías de comunicación de los objetos Los OUTLETS y ACTIONS establecen las vías de comunicación de los objetos. Los outlets identifican objetos y las acciones hacen que los objetos reciban ordenes de otros objetos, como los botones. ConverterController Converter Las vías de comunicación de los objetos Los OUTLETS y ACTIONS establecen las vías de comunicación de los objetos. Los outlets identifican objetos y las acciones hacen que los objetos reciban ordenes de otros Converter.m objetos, como los botones. #import “Converter.h” @implementation Converter - (float)converterAmount:(float)amt atRate:(float)rate { return (amt * rate); } @end Converter.h ConverterController Converter #import <Cocoa/Cocoa.h> @interface Converter:NSObject { } -(float)converterAmount:(float)amt atRate:(float)rate; @end Las vías de comunicación de los objetos ConverterController.m #import “Converter.h” @implementation ConverterController - (IBAction)convert:(id)sender { float rate, amt, total; ConverterController amt=[dollarField floatValue]; rate=[rateField floatValue]; total=[converter converterAmount:amt atRate:rate]; [totalField setFloatValue:total]; [rateField selectText:self]; } @end Resumen Se han mostrado las capas exteriores de los conceptos que se utilizan en programación orientada a objetos. Un objeto es una asociación de datos privados, y de procedimientos que pueden acceder a estos datos. Un mensaje es una solicitud, que indica al objeto que lleve a cabo uno de sus procedimientos. Estos conceptos proporcionan la encapsulación, que significa que los datos de un objeto quedan ocultos en su interior, y protegidos por una cubierta de procedimientos. Resumen Se ha mostrado la forma en que esta pareja de conceptos se puede añadir a un lenguaje convencional de programación, utilizando Objective-C como ejemplo. Los objetos se identifican mediante el nuevo tipo de datos id, y las solicitudes que se hacen a los objetos se escriben en forma de expresiones que contienen mensajes. Se ha mostrado también la forma en que se utilizan los conceptos, y contrastado el nuevo estilo de programación con el estilo convencional. La diferencia primaria estriba en que se ha trasladado una única responsabilidad, que era del consumidor de un servicio, al proveedor de ese servicio, se ha trasladado la responsabilidad de seleccionar el operador (el procedimiento) cuyo tipo es compatible con el tipo de operando (de dato) sobre el cual hay que actuar. Contacto: [email protected] [email protected] Abdiel E. Cáceres González Centro de Investigación y de Estudios Avanzados - IPN México D.F., México. 2004