Conversor de temperatura (2a parte)

Clases y Objetos

Si la parte más vistosa de hacer un programa es definir las ventanas que veremos en el programa, la parte más delicada es definir las clases y objetos que utilizaremos. Una parte de ellos ya nos vienen definidos por la ventana (ventana, campos de texto, etc.) pero también habremos de definir más clases y sobre todo sus relaciones.

Modelo de datos

Tal como hemos dicho lo más difícil cuando hacemos un programa es decidir que objetos vamos a modelar . En este caso lo más sencillo es utilizar el modelo Vista-Datos-Control (MVC en sus siglas inglesas).

El objeto vista será la ventana de la aplicación (que ya hemos definido con Interface Builder: ventana de la aplicación). Para los datos crearemos la clase Temperaturas, que contendrá los elementos necesarios para convertir las temperaturas. Y para el control creamos la clase Gestor, esta clase se encargará de la lógica del programa: cuando se convierte, en que sentido, que campo se activa primero, etc.

Siguiendo la costumbre de la programación orientada a objetos utilizaremos la mayúscula para la primera letra de la clase, y la primera letra en minúscula para los objetos (también llamados instancias de la clase)

Crear las clase Gestor y Temperaturas

  • Siguiendo en Interface builder selecciona la ventana MainMenu.nib y dentro de ella la pestaña de clases (classes)
  • Busca la clase NSObject y aprieta retorno. Esto permite crear una subclase dentro de la clase NSObject que llamaremos Gestor.
    Crear clase
  • Repetir el proceso para crear las clase Temperaturas

Preparar las conexiones entre los objetos

En la programación orientada a objetos un programa se puede entender como una colección de objetos que colaboran entre ellos enviandose mensajes. De forma que ahora queda definir la red de relaciones que se crearan entre los objetos que seran nuestro programa. Esto lo hacemos a nivel de clase, definiendo las
salidas (outlets) posibles de cada clase y sus acciones.

En primer lugar las salidas de la clase Gestor:

  • Con la clase Gestor seleccionada, ves a la ventana de propiedades (Tool > Show Info) y en la pestaña atributos (Attributes) selecciona las salidas (outlets)
  • Añade cuatro Salidas con el boton añadir (Add) que se llamaran temperatura, salidaCelsius, salidaFahrenheit y selector. Una de las ventajas de Objective-C es que no hace falta definir con todo detalle a que tipo de objeto será la salida, sino que esta decisión se puede hacer en tiempo de ejecución. Para ello podemos marcar el tipo genérico id.
    Creando salidas

Después se definen las acciones de la clase Gestor. Estas acciones corresponderan luego a mensajes a los cuales ha de saber responder la clase.

  • En la misma ventana de propiedades selecciona los métodos de la clase
    (actions)
  • Añade un nuevo método (Action) haciendo clic en el botón
    de añadir (Add).
  • Llama convertir al nuevo método. Observa como se añaden los dos puntos (:) al final del nombre

Acciones de la clase Gestor

Crear los objetos

La clase es una definición general que puede servir para crear multiples objetos.

En nuestro caso la identificación entre clase y objeto es muy tentadora, sólo tenemos un objeto de cada clase. Pero con ligeras modificaciones este programa podría trabajar con dos temperaturas diferentes (de diferentes lugares) y en ese caso seria útil tener
dos objetos temperatura de la misma clase, pero con datos diferentes. Al proceso de crear un nuevo objeto a partir de una clase se le llama en Inglés Instantiate.

  • Selecciona la clase Gestor en la ventana MainMenu.nib
  • Con el menú Classes > Instantiate Gestor crea la instancia
    de la clase Gestor
  • Selecciona la clase Temperaturas y crea una nueva instancia de la clase Temperaturas.
  • Ahora en la pestaña de instancias (Instances) de la ventana
    MainMenu.nib se pueden ver los objetos que hemos creado.
    Instanciando objetos

Realizar las conexiones

Si antes al definir la clase hemos indicado unas conexiones posibles (outlets y actions) ahora nos toca concretarlas en los objetos creados. En concreto permitrán que el objeto gestor acceda a los campos de la ventana, al selector del sentido de la conversión y a la temperatura. Igualmente permite que cuando el botón se accione se haga la conversión.

  • En la ventana MainMenu.nib hacer clic en la instancia gestor y apretando la tecla CTRL arrastra el cursor hasta el campo de texto que contendrá la temperatura en Fahrenheit.
  • Con los dos elementos enlazados los has de conectar haciendo clic en la ventana de propiedades en la salida (outlet) salidaFahrenheit
    Conexiones
  • Repite lo mismo para el campo de texto con la temperatura en Celsius
  • Para el selector del tipo de conversión que se tiene que realizar hay que hacer lo mismo, pero cuidando que el enlace sea al conjunto de los dos botones (un objeto NSMatrix) y no a uno de los botones.
  • Para conectar la salida temperatura hay que hacer lo mismo, pero desde el objeto gestor hacia el objeto temperatura.
  • Para la acción convertir es muy similar, pero en sentido contrario: desde el botón hacia el objeto gestor.

Código del programa

Pese a todo el trabajo hecho el programa todavía no hace muchas cosas (bueno, de momento nada…). Para que haga algo más que mostrar una ventana hay que indicar que debe hacer cada objeto.

Crear los ficheros de cada clase

El primer paso es crear los ficheros que contendrán el código. En Objective-C el código se guarda en ficheros de módulos (.m). Además los ficheros de cabeceras (.h) contienen las definiciones de las variables de objeto y de los mensajes que pueden tratar los objetos de la clase. En otros lenguajes de programación (Java por ejemplo) estos mensajes se llaman métodos y son las posibles funciones
que pueden hacer los objetos.

Seguimos con Interface Builder

En la ventana MainMenu.nib escoge la pestaña de clases (classes)
y selecciona la clase Gestor.

Selecciona el menú Classes > Create files for Gestor

En la ventana de selección asegurate que la casillas gestor.h y
gestor.m están seleccionadas

Generar ficheros de la clase

Repite lo mismo para la clase Temperatura

Ya puedes cerrar Interface Builder

Temperaturas. Archivo de cabeceras.

Ahora hay que volver a XCode

Selecciona los cuatro ficheros que hemos creado y arrastralos a la carpeta Classes

Abre (con doble clic o selccionando el icono del editor ) el fichero temperaturas.h

Veamos el código que nos ha creado el programa Interface Builder.

  /* Temperaturas */
#import <Cocoa/Cocoa.h>
@interface Temperaturas : NSObject
{

}

@end

La primera línea es un simple comentario que contiene el nombre de la clase y que podríamos personalizar a nuestro gusto. En Objective-C los comentarios se marcan de la misma forma que en C (abriendo con /* y cerrando con */) o en C++ y Java ( a partir de // se trata como comentario)

La segunda linea es muy importante. La orden import permite incorporar las cabeceras de diferentes clases y por tanto nos permiten utilizarlas en nuestro código. En este caso concreto nos incorpora todas las funciones de la libreria Cocoa.

Hace casí lo mismo que la orden include de C, pero el compilador solo incluirá las clase que se utilicen en el programa y no todas las clases incluidas en el fichero de cabeceras.

La tercera linea nos indica que esto comienza la interficie(interface) de la clase Temperaturas que desciende de la clase NSObject. La interfície es la parte pública y que otras clases y objetos podrán conocer.

Entre las llaves se indicarían las variables de objeto, accesibles para cualquier método del objeto.

Debajo de las llaves se definirán los mensajes (métodos) a los cuales puede responder un objeto de esta clase.

La última linea es una obviedad, indica que ha acabado la interfície de la clase.

Hay que modificar el código para que la clase temperatura sea capaz de realizar las conversiones de un tipo de escala a otro:

  /* Temperaturas */
#import <Cocoa/Cocoa.h>
@interface Temperaturas : NSObject
{

}

- (float)convertirAFahrenheit:(float)temp;
- (float)convertirACelsius:(float)temp;

@end

El signo menos delante de cada una de las lineas nos indica que en un método de objeto. Es decir puede existir un método para cada uno de los objetos de la clase

el primer (float) nos indica que como respuesta al mensaje responderá con un número real (en realidad con un número de coma flotante)

convertirAFahrenheit: y convertirACelsius: son los nombres de cada método que puede tratar un objeto de esta clase. ¡Atención! que el nombre del método incluye los dos puntos (:)

La última parte de cada una de la lineas nos explica que se espera que el mensaje incluya como parámetro un número real.

En resumen se espera que a este objeto le llegue un mensaje con una temperatura en Celsius (o Fahrenheit) y devuelva una temperatura en Faherenheit (o en Celsius). Un ejemplo de como utilizarlo seria:

float temperaturaFinal = [temperatura convertirAFahrenheit:72.0];
//la variable temperaturaFinal valdrá 161,6

Temperaturas. La implementación

El núcleo del programa es la conversión de temperaturas. La implementación sobre como se hace la conversión la haremos en el fichero temperaturas.m que contendrá el siguiente código.

#import "Temperaturas.h"
@implementation Temperaturas

- (float)convertirAFahrenheit: (float)temp{
    return (temp*9.0/5.0+32.0);
}

- (float)convertirACelsius: (float)temp{
 return ( (temp-32)*5.0/9.0);
}

@end

la primera linea es como antes un comentario.

La segunda nos indica que en este fichero esta la implentación de la clase Temperaturas.

La tercera linea es idéntica a la declaración del método que hay en el fichero de cabeceras. El código del método estará entre las llaves.

En este caso el código es bien sencillo calcula la conversión del valor entrado según la fórmula: Tf = TC*5/9 +32 y una vez hecho el cálculo devuelve (return) a quien ha enviado el mensaje. Se ha hecho todo en una única linea, pero también funcionaria el siguiente código.

- (float)convertirAFahrenheit: (float)temp{
    float tf;
    tf = temp*9.0/5.0 + 32.0;
    return tf;
}

Para convertir a Celsius el código es equivalente.

Gestor. Archivo de cabeceras

Para la clase Gestor Interface Builder nos ha creado el siguiente código:

/* Gestor */

#import <Cocoa/Cocoa.h>
@interface Gestor : NSObject{

    IBOutlet id temperatura;
    IBOutlet id salidaCelsius;
    IBOutlet id salidaFahrenheit;
    IBOutlet id selector;
}

- (IBAction)convertir:(id)sender;

@end

Como se ve tenemos en el código las salidas y acciones que hemos definido en pasos anteriores. Cada una de las sentencias que comienzan con IBOutlet nos permitirán acceder a un objeto del tipo IBOutlet (salida) llamado temperatura (o salidaCelsius, o …..)

En cambio la acción convertir: es un método de objeto del tipo IBAction. Como argumento del método espera un objeto genérico (id) al que llamaremos sender . En este caso será el botón que al activarse enviará un mensaje que activará ese método.

Gestor. La implementación

Aunque la conversión de temperaturas sea el núcleo del programa, quien ha de controlar que se convierte, cuando y como es la clase gestor.
Aquí el código es mucho más complejo y requiere muchas más
explicaciones.

Para esta clase Interface Builder nos ha creado el siguiente código

#import "Gestor.h"

@implementation Gestor

- (IBAction)convertir:(id)sender{

}

@end

Lo primero que vamos a hacer es incluir el fichero de cabeceras de la clase Temperaturas:

#import "Temperaturas.h"

Lo siguiente es definir el código del método convertir:

- (IBAction)convertir:(id)sender{

    float TemC = [salidaCelsius floatValue];
    float TemF = [salidaFahrenheit floatValue];

    if([[selector selectedCell] tag ]== 0){
        //Seleccionada la conversión C a F
        TemF = [temperatura convertirAFahrenheit:TemC]
        [salidaFahrenheit setFloatValue:TemF];
        [salidaCelsius selectText:self];
    }
    else{
        TemC = [temperatura convertirACelsius:TemF];
        [salidaCelsius setFloatValue:TemC];
        [salidaFahrenheit selectText:self];
    }
}

float TemC = [salidaCelsius floatValue];

En primer lugar definimos una variable de tipo float llamada TempC. Al mismo tiempo de crear la variable le enviamos el mensaje floatValue al objeto salidaCelsius. Es de esperar que este objeto nos devuelva el valor que el usuario ha entrado
por la pantalla en el apartado de Temperatura Celsius.

float TemF = [salidaFahrenheit floatValue];

Lo mismo hacemos para la temperatura en Fahrenheit, pero con una variable llamada TempF.

if([[selector selectedCell] tag ]== 0)

Para entender mejor la linea(if……) podriamos haber escrito el código de otra forma:

    if([[selector selectedCell] tag ]== 0)....

    Es equivalente a ....

    id celdaSeleccionada = [selector selectedCell];
    int etiqueta = [celdaSeleccionada tag];
    if(etiqueta == 0)

En primer lugar enviamos el mensaje selectedCell a la salida (outlet) selector. selector nos responderá con un objeto que señala al radio botón que está marcado.
Como en principio no conocemos cúal será el tipo del objeto que
nos enviará selector le asignamos a la variable celdaSeleccionada el tipo id, que es un objeto indeterminado y que en el momento de ejecutar el programa se le adjudicará su tipo definitvo. En la variable celdaSeleccionada guardamos la referencia al objeto que nos ha indicado selector.

Lo siguiente es crear una variable de tipo entero llamada etiqueta en la misma linea enviamos un mensaje al objeto referenciado en celdaSeleccionada pidiéndole que nos indique cual es el valor de su etiqueta (tag).

Y en el if comprobamos si el valor de la etiqueta es 0 u otro valor (1) para saber como se tiene que hacer la conversión.

TemF = [temperatura convertirAFahrenheit:TemC];

Si suponemos que la etiqueta nos indica la conversión a Fahrenheit lo siguiente es enviar un mensaje a la salida (outlet) temperatura pidiendole que haga la conversión de temperatura que hemos leído antes de la pantalla.

[salidaFahrenheit setFloatValue:TemF];

Una vez tenemos el valor de la conversión en la variable TempF le enviamos el mensaje setFloatValue a la salida (outlet) salidaFahrenheit con argumento el valor contenido en TempF para que lo muestre por pantalla.

[salidaCelsius selectText:self];

Esta última linea del bloque realmente no es necesaria para que funcione el programa, sólo es un detallito. Hace que una vez hecha la conversión y mostrada por pantalla, se seleccione todo el texto contenido en salidaCelsius para poder entrar otra nueva temperatura con mayor facilidad y sencillez.

Finalmente añadimos el siguiente método:

- (void)awakeFromNib{

    [[salidaCelsius window] makeKeyAndOrderFront:self];

}

Este método es llamado automáticamente por el programa cada vez que se abre la ventana del programa . Y lo que le estamos indicando es que al abrir el programa seleccione la ventana del programa y la ponga en primer plano.

Construir y probar

Una vez que ya tenemos todo el proyecto en marcha, creadas las ventanas y escrito el código solo queda compilar y probar.

Compilar

Icono GCC

El compilador utilizado por Xcode es el conocido gcc (GNU Compiler Collection), que permite compilar código en C, en C++ y en Objective-C entre otros muchos lenguajes. En el entorno de Xcode no hace falta que aprendamos que opciones hay que enviar al compilador. XCode llama al compilador con todos los lementos preparados para compilar.

En general las opciones por defecto funcionan perfectamente y no
hay que tocar nada.

Para compilar basta con activar el icono:Compilar

Si además de compilar queremos ya probarlo podemos utilizar el icono de compilar y ejecutar:Compilar y ejecutar

En caso que el compilador encuentre un error el programa ya no puede ejecutarse y XCode nos avisa de que error se ha encontrado y donde.

Probando

Si todo ha ido bien y el programa ha compilado bien lo podemos ejecutar con el icono anterior de compilar y ejecutar (si esta compilado todo solamente lo ejecutará). O bien a través del menú build | build and run . Entonces deberia aparecer nuestro programa en funcionamiento

El programa funcionando

Y eso es todo por ahora, queda mucho detalles por pulir (idiomas, iconos, ayuda, etc.) pero eso queda para otro día.

8 comentarios to “Conversor de temperatura (2a parte)”

  1. estoy empezando a programar Says:

    saludos soy antonio estoy haciendo tu programa y me marca un error enesta linea

    [salidafahrenheit setFloatValue:TemF];

    error: syntax error before ‘setfloatvalue’

    aun no logro resolverlo
    gracias por tu atencion

  2. luisrey Says:

    Observa las mayúsculas …
    [salida F ahrenheit setFloat V alue:TemF]:

    El compilador distingue las mayúsculas de las minúsculas. En parte es bueno para tener más posibilidades, pero al principio es un coñazo. Por eso es conveniente seguir las reglas:
    – Clases comienzan por mayúsculas
    – Objetos comienzan por minúsculas
    – En las palabras compuestas las siguientes no se pone espacios sino mayúsculas.

    Un saludo

  3. Antonio Says:

    Mil gracias luis rey creo al parecer solo era un punto y coma lo que me hacia falta, muy bueno tu tutorial muy explicado, le batalle un poco porque apenas tengo 4 dias que toco un mac y pues ni mucho menos habia tocado cocoa y pues me toco utilizar el xcode 3,0 y es algo diferente a la version en la que tu explicas, pero me funciono y seguire aprendiendo, un detalle, lo unico que no me daba era esta linea ” if ([[selector selectedCell]tag]==0) ” lo que pasa que en tu tutorial lo comparabas con cero y en mi programa me funciono como que los botones de radio solo tenian un rango entre 1 y 2 con el cero no entraba a la condicion, bueno gracias me despido y espero sigas poniendo esos buenos tutoriales en la web,

    gracias por tu atencion

  4. sires Says:

    quisiera saber una vez que tienes todo compilado como puedes meter todo el código en un solo archivo de forma que pulsando sobre él se ejecute la aplicación, de tal forma que se pueda utilizar en cualquier ordenador como una aplicación más.

    gracias.

  5. David Nuncio Says:

    Hola… enhorabuena por la pagina, me legra mucho ver que cada vez más personas se están decidiendo a programar en mac, y no solo eso, sino que tabmien a publicar sus sitios web.

    estás agregado a mis favoritos y RSS en las entradas. Felicitaciones de nuevo, y me gustaría ver más tutoriales. Gracias

  6. Sarahi Says:

    Holaa!! me dio mucho gusto encontrar este tutorial está muy bueno solo que parece ke tu tienes otra version diferente a la mia del XCode y estoy un poco perdida para eso de crear las clases porke no encuentro la pantalla donde viene el NSObject para poder crear las subclases Gestor y temperatura eske esta muy rarito mi XCode la version es la 3.1.1
    no se si si es por eso o porke soy novata jojo :P
    bueno espero tu respuesta, mil gracias de antemano

  7. Firichi Says:

    Antes de nada quiero Felicitarte por este trabajo tan bien explicado, de forma precisa y simple. Soy nuevo en esto de Cocoa y en Programación orientada a Objeto. he leído muchos tutoriales pero este realmente me aclaró muchas cosas.

    tuve un problemita con lo de la versión del XCode 3.1, pero pude resolverlo y en el camino entendí aún más :-)

    Gracias

  8. nmartinez2 Says:

    Muy bueno ! aunque realmente no me sale del todo :S.

    Pero seguire intentandolo!

    Saludos :)

    /Klanx

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: