P
ROGRAMACIÓNO
RIENTADA AO
BJETOS(P
RACTICA DED
ISEÑO,
CURSO2009-2010)
O
BJETIVOEl objetivo de la práctica es crear una calculadora utilizando el lenguaje Java. Se ha escogido el ejemplo de la calculadora porque permite aplicar fácilmente principios y patrones de diseño, pero manteniendo el programa dentro de un tamaño razonable.
Aunque es posible implementar la calculadora sin seguir estos principios (en pocas clases, siguiendo un paradigma meramente procedimental) es importante resaltar que en la valoración de la práctica se le da más importancia a cómo se hacen las cosas, que a simplemente mirar si las cosas funcionan o no.
F
UNCIONAMIENTO BÁSICO DE UNA CALCULADORAUna calculadora se compone de los siguientes elementos básicos que mostramos en la figura adyacente y que explicamos a continuación:
Pantalla: Muestra los números que se van introduciendo y los resultados de las operaciones.
Operadores unarios: Son operadores que actúan sobre un único operando, que es el que se está mostrando en ese momento por pantalla. Entre ellos destacaremos el cuadrado (X2), raíz cuadrada (SQRT), inversa (1/X) y cambio de signo (+/-)
Operadores binarios: Son operadores que actúan sobre dos operandos, el modo de funcionamiento sería: pulsamos el primer operando, pulsamos el operador binario, pulsamos el segundo operando y pulsamos la tecla “=” para obtener el resultado. Entre los operadores binarios destacaremos la suma(+), la resta (-), la multiplicación (*) y la división (/).
Operaciones de memoria: Permiten guardar datos en la memoria interna de la calculadora. Tenemos cinco posibles operaciones: memory recall (MR) recupera la información de la memoria, memory store (MS) almacena el valor mostrado en la memoria, memory clear (MC) borra el contenido de la memoria, sumar el valor mostrado al contenido de la memoria (M+) y
+/-Pantalla 48 X2 SQRT T 1/X MR MC M+ M-9 6 3 8 5 2 . 7 4 1 0 * -+ = / Operadores binarios C Operadores unarios Operaciones de memoria Dígitos MS M AC Operaciones de borrado 45 + 3 = 48 Ventana de log
restar el valor mostrado al contenido de la memoria (M-). También existe un flag, representado por la letra M que se enciende cuando la memoria tiene algún valor almacenado.
Dígitos: Operandos que se introducen para el cálculo. Existe un botón “.” que permite introducir valores decimales.
Operaciones de borrado: Un botón “AC” permite resetear por completo el estado de la calculadora y dejarla en el estado inicial. Un botón “C” permite borrar el número de la pantalla pero sin anular la operación que se está llevando a cabo.
Ventana de log: La ventana de log permite ver la secuencia de operaciones que hemos ido realizando. Sería el equivalente al rollo de papel que tienen algunas calculadoras.
Para cualquier duda sobre el funcionamiento de la calculadora puede consultarse cualquier calculadora de bolsillo o las calculadoras de los distintos sistemas operativos.
I
NDICACIONES Y RECOMENDACIONES DE DISEÑO Recomendación general
◦ En primer lugar lo más importante de todo es que la calculadora debe hacer uso de las características propias de la programación orientada a objetos (herencia, polimorfismo, sobrecarga, sobreescritura, etc.) utilizando principios y patrones de diseño.
◦ El código desarrollado debe ser flexible y fácilmente ampliable. Por ejemplo, debe ser sencillo añadir nuevas funciones unarias o binarias a la calculadora tocando lo menos posible las otras clases ya existentes.
◦ Se deberá evitar en lo posible el uso de clases “Dios”, es decir, clases que controlen prácticamente todo el programa mientras que las otras clases actúan más como repositorios de información (como si fueran registros de Pascal).
Relaciones de generalización/especialización
◦ Como se puede observar en la descripción de la calculadora es fácil ver a primera vista posibles relaciones de generalización/especialización. Incluso si nos fijamos un poco más puede haber más de las que en principio pudiera parecer.
◦ La relación de herencia permite tratar de forma genérica objetos concretos con todas las ventajas que ello conlleva. Además la ligadura dinámica asegura que el código a ejecutar siempre es el adecuado.
Máquina de estados
◦ Pulsar las mismas teclas puede no tener el mismo funcionamiento dentro de la calculadora. Por ejemplo si pulsamos “5 + 3” si ahora pulsamos “–” la calculadora acumula el resultado de la operación anterior mostrando un “8” y se queda a la espera del segundo operando. Si pulsamos “5 +” y ahora pulsamos “–” la calculadora entenderá que queremos reemplazar la operación “+” por la operación “–”.
◦ Este comportamiento suele implementarse suponiendo a la calculadora una máquina de estados y, dependiendo en que estado estemos, la pulsación sobre un botón tendrá un funcionamiento u otro.
Uso de pilas
◦ Una forma de manejar expresiones aritméticas es utilizando pilas (normalmente visto en la asignatura de EDI). Podéis considerar su utilización para, por ejemplo, almacenar valores y operadores antes de pulsar “=” y proceder a su cálculo.
Interfaz del usuario
◦ Un aspecto importante del interfaz del usuario es debe estar desligado de las clases que representan el dominio de la aplicación, siguiendo los principios de diseño que se han comentado en clase. Para mantener actualizados simultáneamente la pantalla y la ventana de log pueden utilizarse estrategias de notificación por parte del dominio al interfaz. ◦ El interfaz debe estar realizado con los componentes javax.swing y debe usar layout
managers para su disposición en la ventana (podéis usar el diseñador de NetBeans).
Uso de números decimales
◦ Los valores numéricos deberán representarse con clases como java.math.BigDecimal y no con clases en coma flotante como double o float. Esto es debido a que la propia representación en coma flotante puede hacer que aparezcan problemas de precisión. Es decir, al acabar una serie de operaciones podemos encontrarnos con que en vez de un “1” obtenemos un “0.99999”.
◦ Básicamente un BigDecimal es un valor decimal en coma fija. La forma recomendada para crear un valor de este tipo es usar el constructor a partir de Strings “BigDecimal(String val)” ya que el constructor a partir de valores en coma flotante puede presentar problemas de precisión.
◦ Una vez creado tenemos que tener en cuenta que el objeto es inmutable, por lo que todas las operaciones con el mismo crearán un nuevo objeto.
◦ En las operaciones, por ejemplo la división “divide(BigDecimal divisor, int scale, RoundingMode roundingMode)”, es necesario tener en cuenta la escala (básicamente el número de dígitos a la derecha de la coma) y el modo de redondeo. Para este último aunque existen unas constantes en la clase BigDecimal se consideran obsoletas y se recomienda el uso del enumerado java.math.RoudingMode. El método de redondeo por defecto en las calculadoras suele ser el HALF_UP, es decir, se redondea al vecino más cercano y en caso de equidistancia se redondea hacia arriba. Así el valor 2,3333 se redondea a 2,33, el valor 2,7777 se redondea a 2,78 y el valor 2,5555 se redondea a 2,56. ◦ Por ejemplo el resultado de ejecutar el siguiente código es 2.66667
BigDecimal num1 = new BigDecimal("8"); BigDecimal num2 = new BigDecimal("3");
BigDecimal num3 = num1.divide(num2, 5, RoundingMode.HALF_UP); System.out.println(num3);
◦ Tener en cuenta que las operaciones pueden producir errores si no se da una escala válida en la que representar los resultados.
Posibles mejoras
◦ A continuación cito una serie de mejoras a la descripción original de la práctica cuya inclusión es optativa pero que, de realizarlas, tendrán un reflejo final en la nota:
◦ Implementaciones de nuevos operadores, tanto unarios (seno, coseno, tangente, logaritmo neperiano, etc.) como binarios (módulo).
◦ Visor de resultados en distintos modos (binario, octal, hexadecimal y, por supuesto, decimal)
◦ Utilización de paréntesis
C
ARACTERÍSTICAS DEL CÓDIGO A ENTREGAR El programa consistirá en un proyecto de la herramienta NetBeans (en su versión instalada
en el laboratorio de prácticas). El programa debe funcionar correctamente en las máquinas
de docencia.
El proyecto NetBeans se denominara de la siguiente forma
“ApellidoA1-ApellidoA2-ApellidoA3-ApellidoA4-Gxx”. Donde ApellidoAx es el primer apellido de los componentes
del grupo con la única restricción de que el primero que aparezca tiene que ser el primero por orden de lista y que será el encargado de entregar la práctica. Gxx será el número del grupo de prácticas. Así, por ejemplo, la práctica de los alumnos Almagro, Garcia, Martinez, Torre y cuyo grupo de prácticas es el 2.4 se entregará como “Almagro-Garcia-Martinez-Torre-G24”.
El grupo de prácticas al que pertenecéis debería estar en la lista incluida en el Campus Virtual.
El proyecto NetBeans debe tener configurada correctamente la “MainClass” del mismo de tal forma que pulsando F6 (Run Main Project) este se ejecute correctamente.
El código debe incluir pruebas JUnit del mismo aunque no es necesario incluir los mensajes de texto que poníamos en los boletines.
El proyecto debe venir con el software de Cobertura integrado de tal forma que se pueda
generar una cobertura de los tests. La cobertura de los mismos debe tener un valor alto para
que pueda ser considerada en la nota.
C
ARACTERÍSTICAS DE LA MEMORIA A ENTREGAR El código deberá venir acompañado de una memoria escrita. Debido a que muchas veces esta memoria tiene un formato realmente ilegible (letras enormes, código presentado con letra no proporcional, folios sueltos o unidos por una grapa, etc.) os presento a continuación una serie de recomendaciones a la hora de formatear correctamente la práctica. Son recomendaciones, si por algún motivo no podéis cumplir alguna no pasa nada, pero recordar que en la evaluación de la práctica se valora la correcta presentación de la misma:
◦ Encanutillado en espiral, tipo de letra Times New Roman tamaño 12 para los textos y proporcional (Courier New o Monospaced) y de tamaño 8 para el código, espaciado simple e impresión a doble cara.
◦ Impresión del código en un formato que resalte la sintaxis del mismo. Esto es fácil hacerlo desde NetBeans, simplemente escoger la opción “File Print…”. Como el tipo de letra por defecto es de tamaño 10 sería conveniente reducirlo pulsando en “Print Options” y seleccionar el botón para cambiar el tipo de fuente (en el icono de la opción “Text” o usando la combinación de teclas Alt+K). También puedes usar la opción “Print to HTML…” y luego abrir el fichero HTML con la sintaxis resaltada en un editor. ◦ La ordenación de las clases en la memoria será por orden alfabético de paquetes y luego
de nombres de clases dentro del paquete (igual que NetBeans). Cuando hagáis referencia a una clase indicar en qué paquete está para que sea fácilmente localizable.
La estructura de la memoria será la que se detalla a continuación ◦ Primera página
Título de la práctica, nombre de la asignatura y curso, y nombre de todos los
integrantes del grupo de prácticas poniendo en primer lugar al portavoz del mismo (el primero por orden alfabético). Recordar que si vuestro nombre no aparece
en la lista de autores entenderemos que no habéis participado en la elaboración de la práctica (aunque vuestro nombre aparezca en la lista de grupos de prácticas).
◦ Resumen
Resumen breve en el que describáis sucintamente el contenido de la práctica. Deberá incluir los patrones de diseño utilizados en la misma, las mejoras opcionales que habéis incluido en la misma, el funcionamiento básico de la aplicación, etc. No debería extenderse más allá de una carilla y podrá hacer mención al diagrama general de clases incluido a continuación.
◦ Diagrama general de clases
Será un diagrama en el que se muestran todas las clases propias desarrolladas. Para simplificar el diagrama podéis mostrar sólo el nombre de las clases, aunque si es posible mostrar de forma legible atributos y métodos (excluyendo detalles como tipos de retorno o parámetros) podéis hacerlo. El objetivo de este diagrama es hacerse una idea global del diseño y mostrar de forma detallada las relaciones entre las distintas clases.
Aunque los detalles de las clases pueden estar ocultos en aras de la claridad los
detalles de las relaciones deben estar completos y ser claros.
Recordar que es importante que todo diagrama que presentéis tiene que ser legible. ◦ Explicación de subsistemas:
Un subsistema se entenderá como un conjunto de clases relacionadas entre sí (por ejemplo, las clases implicadas en el funcionamiento de un patrón de diseño). Para explicar cada subsistema hay que incluir lo siguiente:
Explicación detallada del funcionamiento del subsistema, de las clases que lo componen, etc. Esta explicación deberá ir acompañada de los diagramas de clases y de secuencia que se comentan a continuación. Es importante que el texto y los
diagramas formen un todo relacionado, es decir, el texto debe de comentar y hacer referencia a lo que vemos en los diagramas.
Un diagrama de clases que muestre todos los detalles de las mismas, incluyendo los detalles de los métodos, los atributos, las asociaciones, las relaciones de dependencia, etc. En este caso sólo contemplaremos relaciones entre clases del subsistema (las relaciones entre todas las clases ya las hemos visto en el diagrama general de clases). En la relaciones de asociación habrá que incluir adornos como nombres de rol, multiplicidades, navegabilidad, visibilidad, etc.
Diagramas de secuencia para las operaciones más importantes de dicho subsistema, o para aquellas que se considere que facilitan la comprensión del funcionamiento dinámico. Los diagramas de secuencia deben ser una fiel representación de los algoritmos escritos en Java. Si por razones de legibilidad se realizan simplificaciones deberían ser indicadas en el diagrama con la correspondiente nota. Se hará especial hincapié en la utilización de las novedades que la versión 2.0 de UML introdujo en este tipo de diagramas (por ejemplo, el uso de fragmentos – frames –).
F
ECHA Y FORMA DE ENTREGA La fecha límite de entrega de la práctica es el 5 de Febrero de 2010.
En el Campus Virtual se habilitará un espacio para que el portavoz de la práctica la entregue. Deberá entregarse un fichero ZIP que incluya el directorio del proyecto NetBeans y un directorio “Memoria” que incluya el fichero original de la memoria (DOC, ODT, etc.) y el
mismo convertido a PDF. En nombre de los ficheros de la memoria seguirá la misma
La memoria impresa de las prácticas se entregará a los profesores de la asignatura durante las horas de tutorías, en la realización del examen final y, excepcionalmente, el último día podéis depositarla en el casillero situado detrás de conserjería.
V
ALORACIÓN La valoración de la práctica constará de los siguientes apartados: ◦ Documentación (1 punto)
Se entrega todo lo que se pide y como se pide
La memoria es legible, tiene un formato claro, está bien presentada, etc. ◦ Diseño (3 puntos)
Uso adecuado de las características propias de la POO: herencia, polimorfismo, composición, etc.
Uso adecuado de principios y patrones de diseño
Los comentarios que acompañan a las explicaciones del diseño son claros, precisos y lo suficientemente extensos para comprender lo que se está realizando
◦ UML (3 puntos)
Calidad, detalle y adecuación al código de los diagramas de clase Calidad, detalle y adecuación al código de los diagramas de secuencia ◦ Funcionamiento general (3 puntos)
Compila, funciona sin problemas y cumple las especificaciones iniciales El interfaz gráfico está correctamente realizado
Qué mejoras y/o ampliaciones se han realizado
Se han realizado suficientes pruebas JUnit y su cobertura es adecuada
A
SPECTOS IMPORTANTES A TENER EN CUENTA Recordar que es necesario sacar una nota igual o superior a cuatro para poder aprobar la
asignatura.
Aquellas prácticas de las cuales se tenga constancia que han sido copiadas (de los compañeros, de Internet, etc.) serán calificadas con un cero. En el caso de copias entre
compañeros tanto el original como la copia recibirán un cero.
Después de entregar la práctica si el profesor lo considera necesario puede llamar a cualquier grupo para que pase a defender dicha práctica. La no asistencia al acto de defensa de la práctica o si se constata en dicho acto que el conocimiento de la práctica por parte del alumno es insuficiente implicará el suspenso de dicha práctica y, por lo tanto, de la asignatura.
En las convocatorias de Septiembre y Diciembre de esta asignatura la práctica a entregar será la misma. Por ello, se conserva la nota de la práctica obtenida en la convocatoria ordinaria, a no ser que el alumno quiera incluirle mejoras en cuyo caso deberá volverla a entregar (incluyendo la documentación). Las fechas límites de entrega en las convocatorias de Septiembre y Diciembre coincidirán con la fecha del examen teórico.