ALGORITMICA Y PROGRAMACION POR OBJETOS I Nivel 6 Manejando Estructuras Contenedoras de 2 Dimensiones y Persistencia Marcela Hernández Hoyos Motivación BrazoMecanico bodega ArrayList 0 .. n Bodega 0 .. n columnas 0 1 filas 2 maxX … columnas filas :Cubo Bodega ArrayList Motivación BrazoMecanico bodega ArrayList 0 .. n Bodega 0 .. n columnas 0 1 filas 2 maxX … columnas filas :Cubo Bodega ArrayList Motivación :Cubo fila i Bodega columna j Ubicado en la posición i,j Motivación Cómo definir este tipo de estructura fila i Bodega columna j Motivación Cómo definir este tipo de estructura R// Con una MATRIZ fila i Bodega columna j Qué vamos a aprender en este nivel: nCómo definir, crear y manipular matrices (estructuras contenedoras de 2 dimensiones) oPatrones de recorrido de matrices pEsquema simple de persistencia (archivos) Caso de estudio No. 1: Visor de Imágenes El Visor de Imágenes • • • • Funcionalidad Interfaz usuario Formato BMP Mundo El Visor de Imágenes Funcionalidad • • Permite la visualización de imágenes en formato BMP Ofrece servicios de transformación de la imagen: – – – – – Transformar la imagen en su negativa Aplicar un filtro Rotar la imagen Binarizar la imagen … Formato BMP Pixel tiene un color Rojo Verde Azul 0..255 0..255 0..255 El Visor de Imágenes – Interfaz usuario El Visor de Imágenes – Mundo IMAGEN: Único elemento del mundo Rojo Verde Azul Se representa por medio de una MATRIZ de 300x400 Contenedoras de 2 dimensiones: MATRICES El Visor de Imágenes – Mundo Único atributo de la clase Imagen llamado bitmap (nombre de la matriz) Clase nativa de Java Imagen bitmap [300] [400] Color int R int G int B Así se indica en UML que se trata de una matriz (contenedora de 2 dimensiones) Un objeto de la clase Imagen podría verse de la siguiente manera : Imagen 0 0 Color bitmap = 1 2 … 399 Cada elemento de la matriz apunta a un objeto de tipo Color 1 … 299 : Color R = 255 G=0 B=0 Matriz = contenedora de 2 dimensiones de tamaño fijo bitmap [0][1] bitmap [0][399] bitmap [0][0] columnas filas bitmap [1][0] 0 1 2 … • 399 0 bi 1 … if bitmap = 299 bitmap [299][0] bitmap [299][399] Matriz = contenedora de 2 dimensiones de tamaño fijo columnas filas 0 0 1 … bitmap = 299 1 2 … 399 • Cada posición de la matriz (casilla) se utiliza como una variable bitmap[2][3] = new Color(0,0,0); if ( bitmap[ i ][ j ] == bitmap[ 0 ][ 0 ] ) … Declaración de una matriz Se declaran public class Imagen constantes para fijar el tamaño de la { matriz //Constantes final public static int ANCHO_MAXIMO = 400; final public static int ALTO_MAXIMO = 300; //Atributos private Color [ ][ ] bitmap; } Declaración de una matriz public class Imagen { //Constantes final public static int ANCHO_MAXIMO = 400; final public static int ALTO_MAXIMO = 300; //Atributos private Color [ ][ ] bitmap; } bitmap es una matriz de dos dimensiones de tamaño fijo y cuyos elementos son TODOS de tipo Color Declaración de una matriz public class Imagen { //Constantes final public static int ANCHO_MAXIMO = 400; final public static int ALTO_MAXIMO = 300; //Atributos private Color [ ][ ] bitmap; } Color es una clase de JAVA (paquete java.awt) Declaración de una matriz public class Imagen { //Constantes final public static int ANCHO_MAXIMO = 400; final public static int ALTO_MAXIMO = 300; //Atributos private Color [ ][ ] bitmap; } También se puede escribir private Color bitmap [ ][ ] Inicialización de una matriz public Imagen { bitmap = new Color [ALTO_MAXIMO] [ANCHO_MAXIMO]; } • El espacio en memoria (una cajita por posición de la matriz) queda reservado. • El valor de los elementos del arreglo es indefinido al comienzo (null). • Para consultar el número de filas de la matriz: length bitmap.length • Para consultar el número de columnas de la matriz: bitmap[0].length • Si se trata de acceder a una casilla con un par de índices no válidos java.lang.ArrayIndexOutOfBoundsException Acceso a los elementos de una matriz (ejemplo de recorrido total) public void ImagenAzul ( ) { // Recorre la matriz inicializando las casillas con // objetos de tipo Color cuyo valor representa el azul for ( int i = 0; i < ALTO_MAXIMO; i++ ) { for ( int j = 0; j < ANCHO_MAXIMO; j++ ) { bitmap[ i ][ j ] = new Color( 0, 0, 255); } } } Se necesitan 2 índices para acceder a una posición de la matriz: • i: fila • j: columna Entonces… Se necesitan 2 ciclos para recorrer la matriz: • recorrido de public void ImagenAzul ( ) TODAS las filas { • para cada fila, // Recorre la matriz inicializando las casillas con recorrido de // objetos de tipo Color cuyo valor representa el azul TODAS las columnas for ( int i = 0; i < ALTO_MAXIMO; i++ ) { for ( int j = 0; j < ANCHO_MAXIMO; j++ ) { bitmap[ i ][ j ] = new Color( 0, 0, 255); } } } Entonces… public void ImagenAzul ( ) { // Recorre la matriz inicializando las casillas con // objetos de tipo Color cuyo valor representa el azul } for ( int i = 0; i < ALTO_MAXIMO; i++ ) { for ( int j = 0; j < ANCHO_MAXIMO; j++ ) { bitmap[ i ][ j ] = new Color( 0, 0, 255); } } filas Los índices van desde 0 hasta el número de elementos menos 1 columnas Ahora con while… public void ImagenAzul ( ) { // Recorre la matriz inicializando las casillas con // objetos de tipo Color cuyo valor representa el azul int i = 0; while (i < ALTO_MAXIMO) { int j = 0; while ( j < ANCHO_MAXIMO ) { bitmap[ i ][ j ] = new Color( 0, 0, 255); j++; } filas i++; } fila columna } columnas Cómo comparar los elementos de una matriz (o arreglo) ? • Si los elementos de una matriz son de tipo simple (enteros, reales, etc.) – Se comparan con el operador ==. • Si los elementos NO son de tipo simple, sino que son objetos, en la matriz se guarda una referencia (apuntador) al objeto y NO el objeto mismo. En este caso, hay que compararlos con equals. Un objeto de la clase Imagen podría verse de la siguiente manera : Imagen 0 0 1 2 … 399 Cada elemento de la matriz es UNA REFERENCIA (apunta) a un objeto de tipo Color Color bitmap = 1 … 299 : Color R = 255 G=0 B=0 Ejemplo de comparación : Color : Color : Color R = 255 G=0 B=0 R = 255 G=0 B=0 R = 255 G=0 B=0 : Imagen 0 1 2 0 Color bitmap = 1 : Color : Color : Color R=0 G=0 B = 255 R=0 G=0 B = 255 R=0 G=0 B = 255 • Imagen de 2 x 3 • Primera fila de coloreada de rojo (255,0,0) • Segunda fila coloreada de azul (0,0,255) • Cada casilla tiene un objeto DIFERENTE que representa el color del pixel Ejemplo de comparación : Color : Color : Color R = 255 G=0 B=0 R = 255 G=0 B=0 R = 255 G=0 B=0 : Imagen V F 0 1 2 bitmap[0][0] == bitmap[0][1] 0 bitmap[0][0] .equals(bitmap[0][1]) Color bitmap = bitmap[0][0] .equals(bitmap[1][0]) 1 : Color : Color : Color R=0 G=0 B = 255 R=0 G=0 B = 255 R=0 G=0 B = 255 Ejemplo de comparación : Color : Color : Color R = 255 G=0 B=0 R = 255 G=0 B=0 R = 255 G=0 B=0 : Imagen V F 0 1 2 bitmap[0][0] == bitmap[0][1] 0 X bitmap[0][0] .equals(bitmap[0][1]) X Color bitmap = bitmap[0][0] .equals(bitmap[1][0]) 1 : Color : Color : Color R=0 G=0 B = 255 R=0 G=0 B = 255 R=0 G=0 B = 255 X Ejemplo de asignación : Color : Color : Color R = 255 G=0 B=0 R = 255 G=0 B=0 R = 255 G=0 B=0 : Imagen 0 1 Color temp = bitmap[0][0]; 2 0 Color bitmap = 1 : Color : Color : Color R=0 G=0 B = 255 R=0 G=0 B = 255 R=0 G=0 B = 255 • Qué es temp? • Qué valor tiene? • Cómo se puede visualizar? • Es un objeto o una referencia? Ejemplo de asignación : Color R = 255 G=0 B=0 Color temp = bitmap[0][0]; : Imagen 0 1 2 0 Color bitmap = 1 temp • Qué es temp? R// Una variable • Qué valor tiene? R// el valor de bitmap[0][0] • Es un objeto o una referencia? R// Es una referencia al mismo objeto que es referenciado por bitmap[0][0] Ejemplo de asignación : Color R = 255 G=0 B=0 Color temp = bitmap[0][0]; : Imagen 0 1 2 0 Color bitmap = 1 temp CONCLUSION: • TODAS las variables que tienen como valor un objeto, son en realidad UNA REFERENCIA (apuntador) al objeto Ejemplo de asignación : Color R = 255 G=0 B=0 Color temp = bitmap[0][0]; : Imagen 0 1 2 0 Color bitmap = 1 temp En este caso: temp y bitmap[0][0] referencian el mismo objeto Ejemplo de asignación : Color R = 255 G=0 B=0 Color temp = bitmap[0][0]; : Imagen 0 1 2 0 Color bitmap = 1 temp Por lo tanto, las expresiones: temp == bitmap[0][0] es verdadera Temp.equals(bitmap[0][0]) es verdadera también Tarea No. 1 (for y while) • Recorrido total para contar el número de píxeles (puntos) de la imagen que tienen el mismo color del dado como parámetro (usando equals de la clase Color !!!) public int cuantosPixelColor( Color colorBuscado) { } Patrones de recorrido de matrices Patrón de recorrido total • Se usa cuando se necesita recorrer TODA la matriz • Ejemplos: – Contar cuántos puntos en la imagen son rojos – Cambiar el color de todos los puntos en la imagen haciéndolos mas oscuros – Cambiar cada color de la imagen por su negativo – Contar cuántos puntos en la imagen tienen una componente roja distinta de cero Patrón de recorrido total • Se usan DOS ciclos: – Un primer ciclo recorre las filas – Por cada fila, un segundo ciclo recorre sus columnas Patrón de recorrido total Indice del primer ciclo empieza en CERO for ( int i = 0; i < NUM_FILAS; i++) { for ( int j = 0; j < NUM_COLUMNAS; j++) { <cuerpo del ciclo> } } Primer ciclo: recorrido de filas Patrón de recorrido total Condición para continuar: índice menor que el número de filas for ( int i = 0; i < NUM_FILAS; i++) { for ( int j = 0; j < NUM_COLUMNAS; j++) { <cuerpo del ciclo> } } Primer ciclo: recorrido de filas Patrón de recorrido total Avance: incremento en 1 del índice for ( int i = 0; i < NUM_FILAS; i++) { for ( int j = 0; j < NUM_COLUMNAS; j++) { <cuerpo del ciclo> } } Primer ciclo: recorrido de filas Patrón de recorrido total Indice del primer ciclo empieza en CERO for ( int i = 0; i < NUM_FILAS; i++) { for ( int j = 0; j < NUM_COLUMNAS; j++) { <cuerpo del ciclo> } } Segundo ciclo: recorrido de columnas Patrón de recorrido total Condición para continuar: índice menor que el número de columnas for ( int i = 0; i < NUM_FILAS; i++) { for ( int j = 0; j < NUM_COLUMNAS; j++) { <cuerpo del ciclo> } } Segundo ciclo: recorrido de columnas Patrón de recorrido total Avance: incremento en 1 del índice for ( int i = 0; i < NUM_FILAS; i++) { for ( int j = 0; j < NUM_COLUMNAS; j++) { <cuerpo del ciclo> } } Segundo ciclo: recorrido de columnas Ejemplo: Modificación de la matriz for ( int i = 0; i < ALTO_MAXIMO; i++) { for ( int j = 0; j < ANCHO_MAXIMO; j++) { bitmap[ i ][ j ] = bitmap[ i ][ j ].darker( ); } } Oscurece TODA la imagen con el método darker de la clase Color Ejemplo: Cálculo sobre la matriz (uso de acumulado) public int rojosCero( ) { int cuantosRojoCero = 0; for ( int i = 0; i < ALTO_MAXIMO; i++) { for ( int j = 0; j < ANCHO_MAXIMO; j++) { Color c = bitmap[ i ][ j ]; if ( c.getRed() == 0 ) { cuantosRojoCero++; } } } return cuantosRojoCero; } Cuenta cuántos puntos tienen el componente rojo igual a cero. Tarea No. 2 • Recorrido total para modificar los puntos de la matriz, convirtiéndolos en sus negativos. El negativo se calcula restándole 255 a cada componente RGB del color y tomando el valor absoluto del resultado. public void negativoImagen( ) { } Tarea No. 2 • Recorrido total para calcular la tendencia de color de la imagen. Esto se calcula de la siguiente manera: un pixel tiene un color de tendencia roja, si su índice es mayor que los otros dos. Lo mismo sucede con los demás colores. Este método retorna 0 si la imagen no tiene ninguna tendencia, 1 si la tendencia es roja, 2 si la tendencia es verde y 3 si la tendencia es azul. public int calcularTendencia( ) { } Patrón de recorrido parcial • Se usa cuando se NO necesita recorrer TODA la matriz • Existe una condición que debemos verificar en cada iteración para saber si debemos detener los ciclos o volver a repetirlos • Ejemplos: – Saber si hay al menos un punto negro (0,0,0) en la imagen –… –… –… Patrón de recorrido parcial (1) Uso de un mismo centinela en los dos ciclos boolean termino = false; for ( int i = 0; i < NUM_FILAS && !termino ; i++) { for ( int j = 0; j < NUM_COLUMNAS && !termino; j++) { <cuerpo del ciclo> if ( <problema terminado> ) termino = true; } } La variable termino puede ser remplazada por cualquier condición que indique el punto en el que el problema ya ha sido resuelto y se deben interrumpir los dos ciclos Ejemplo (1) public boolean hayPuntoNegro( ) { boolean termino = false; for ( int i = 0; i < ALTO_MAXIMO && !termino ; i++) { for ( int j = 0; j < ANCHO_MAXIMO && !termino; j++) { if ( bitmap[ i ][ j ].equals(Color.BLACK) ) { Dice (verdadero termino = true; o falso) si hay al } menos un punto } } negro en la return termino; imagen. } Patrón de recorrido parcial (2) Uso de un centinela diferente para cada ciclo boolean termino1 = false; for ( int i = 0; i < NUM_FILAS && !termino1 ; i++) { boolean termino2 = false; for ( int j = 0; j < NUM_COLUMNAS && !termino2; j++) { <cuerpo del ciclo> • Con termino1 se maneja el if ( <problema interno terminado> ) recorrido parcial del ciclo externo termino2 = true; • Con termino2 se maneja el ciclo } interno if ( <problema externo terminado> ) • termino1 y termino2 pueden ser termino1 = true; remplazadas por condiciones } Ejemplo (2) public boolean hayMuchasFilasConPixelNegro( ) { boolean termino1 = false; int numFilas = 0; for ( int i = 0; i < ALTO_MAXIMO && !termino1 ; i++) { boolean termino2 = false; for ( int j = 0; j < ANCHO_MAXIMO && !termino2; j++) { if ( bitmap[ i ][ j ].equals(Color.BLACK) ) { numFilas++; Dice (verdadero termino2 = true; } o falso) si hay } mas de 50 filas if ( numFilas > 50 ) en la imagen con termino1 = true; un punto negro. } return termino1; } Tarea No. 3 • Filtrar una imagen con un filtro promedio. En el proceso de adquisición de una imagen, ésta puede quedar con una serie de errores los cuales hacen que se vea de mala calidad. Para corregir estos errores existe un algoritmo de filtrado, el cual está basado en el cálculo de un nuevo valor para cada píxel de la imagen. Este valor es calculado como el promedio de los 8 vecinos del píxel en la imagen original, sobre cada uno de los componentes RGB. En este proceso no se incluyen los bordes de la imagen, puesto que no tienen los 8 vecinos necesarios. Este método de la clase Imagen debe retornar una matriz con una copia de la imagen filtrada. Tarea No. 3 • Este método de la clase Imagen debe retornar una matriz con una copia de la imagen filtrada. public Color [ ] [ ] imagenFiltrada( ) { } Tarea No. 3 • Escriba un método de la clase Imagen que modifique la matriz de píxeles de la siguiente manera: si la suma de los tres componentes RGB de un píxel es menor que 100, lo debe remplazar por el color blanco (0,0,0). En caso contrario lo remplaza por el color negro (255,255,255). public void binarizar( ) { } Tarea No. 3 • Escriba un método de la clase Imagen que sea capaz de rotarla 90 grados a la derecha. public void rotar90AlaDerecha( ) { } Otros algoritmos de recorrido • Son adaptaciones de los patrones de recorrido total y parcial. • Ejemplos: – Recorrer sólo una fila – Recorrer sólo una columna – Recorrer sólo la diagonal Ejemplo: Recorrer sólo una fila public int contarVerdes( int numFila) { int numVerdes = 0; for ( int j = 0; j < ANCHO_MAXIMO; j++) { if ( bitmap[ numFila ][ j ].getGreen( ) == 255 ) numVerdes++; } return numVerdes; } Cuenta el numero de píxeles de la fila “numFila” cuyo componente verde es el máximo posible (255). Ejemplo: Recorrer sólo una columna public int darSumaAzulColumna( int numColumna) { int acumAzul = 0; for ( int i = 0; i < ALTO_MAXIMO; i++) { la suma acumAzul += bitmap[ i ][ numColumna ].getBlue(Calcula ); } del valor azul return acumAzul; de todos los } píxeles de la columna “numColumna”. Ejemplo: Recorrer sólo la diagonal public boolean negroEnDiagonal( ) { for ( int i = 0; i < ALTO_MAXIMO && i < ANCHO_MAXIMO; i++) { if ( bitmap[ i ][ i ].equals( Color.BLACK ) ) return true; } return false; Indica (true / false) si } hay un pixel negro sobre la diagonal de la imagen. Caso de estudio: Campeonato de Fútbol El Campeonato de Fútbol • Funcionalidad – Requerimientos funcionales • Interfaz usuario • Mundo del problema • Persistencia El Campeonato de Fútbol Funcionalidad • • Manejo de los resultados de los partidos de un campeonato de fútbol. En el campeonato: – – • • Hay varios equipos. Cada equipo puede jugar contra cada uno de los otros equipos una sola vez. Información de los equipos está en un archivo. La aplicación debe permitir: – – – Registrar el resultado de los partidos Mostrar la tabla de goles Mostrar la tabla de posiciones El Campeonato de Fútbol – Requerimientos Funcionales R1 Registrar el resultado de un partido R2 Leer la información del campeonato de un archivo de entrada R3 Presentar la tabla de goles R4 Presentar la tabla de posiciones R1 R2 R3 R4 El Campeonato de Fútbol – Requerimientos Funcionales • RF1: Registrar el resultado de un partido • Entradas: – – – – Equipo 1 Equipo 2 Goles del equipo 1 Goles del equipo 2 El Campeonato de Fútbol – Requerimientos Funcionales • RF2: Leer la información del campeonato de un archivo de entrada • Entradas: – Nombre del archivo El Campeonato de Fútbol – Requerimientos Funcionales • RF3: Presentar la tabla de goles • Entradas: Ninguna El Campeonato de Fútbol – Requerimientos Funcionales • RF4: Presentar la tabla de posiciones • Entradas: Ninguna El Campeonato de Fútbol – Mundo Campeonato int maxEquipos 0 .. maxEquipos equipos tablaGoles maxEquipos X maxEquipos Gol Equipo String nombre El Campeonato de Fútbol – Mundo Campeonato int maxEquipos 0 .. maxEquipos equipos Equipo String nombre tablaGoles maxEquipos X maxEquipos Gol Campeonato En implementación real, NO hay clase Gol. tablaGoles es una matriz de enteros int tablaGoles = El Campeonato de Fútbol – Mundo La clase Campeonato public class Campeonato { //Constantes final public static int SIN_JUGAR = -1; final public static int INVALIDO = -2; //Atributos private int maxEquipos; private int [ ] [ ] tablaGoles; private Equipo[ ] equipos; } La clase Campeonato public class Campeonato { //Constantes final public static int SIN_JUGAR = -1; final public static int INVALIDO = -2; //Atributos private int maxEquipos; private int [ ] [ ] tablaGoles; private Equipo[ ] equipos; } Los equipos son modelados como un arreglo de tamaño fijo La clase Campeonato public class Campeonato { //Constantes final public static int SIN_JUGAR = -1; final public static int INVALIDO = -2; //Atributos private int maxEquipos; private int [ ] [ ] tablaGoles; private Equipo[ ] equipos; } La información de los equipos se lee de un archivo: NO ES CONSTANTE, se modela con un atributo La clase Campeonato public class Campeonato { //Constantes final public static int SIN_JUGAR = -1; final public static int INVALIDO = -2; //Atributos private int maxEquipos; private int [ ] [ ] tablaGoles; private Equipo[ ] equipos; } La tabla de goles es una matriz CUADRADA (número de filas = número de columnas = número de equipos) La tabla de goles int tablaGoles = equipo1 equipo2 tablaGoles [ equipo1 ] [ equipo2 ] = número de goles que el equipo1 hizo al equipo2 1 2 La tabla de goles int tablaGoles = equipo2 equipo1 tablaGoles [ equipo2 ] [ equipo1 ] = número de goles que el equipo2 hizo al equipo1 2 1 La tabla de goles int tablaGoles = equipo1 equipo1 tablaGoles [ equipo1 ] [ equipo1 ] = INVALIDO (constante = -2) 1 1 Un equipo NO puede jugar contra el mismo La clase Campeonato public class Campeonato { //Constantes final public static int SIN_JUGAR = -1; final public static int INVALIDO = -2; //Atributos private int maxEquipos; private int [ ] [ ] tablaGoles; private Equipo[ ] equipos; } Indica que el partido es INVALIDO porque un equipo NO puede jugar contra si mismo La tabla de goles int tablaGoles = INVALIDO INVALIDO INVALIDO INVALIDO La diagonal de la matriz tiene SIEMPRE el valor INVALIDO (= -2) La tabla de goles int tablaGoles = equipo1 ? equipo2 tablaGoles [ equipo1 ] [ equipo2 ] = SIN_JUGAR ( constante = -1 ) Si un partido NO se ha jugado, NO se conoce el resultado, este es SIN_JUGAR La clase Campeonato public class Campeonato { //Constantes final public static int SIN_JUGAR = -1; final public static int INVALIDO = -2; //Atributos private int maxEquipos; private int [ ] [ ] tablaGoles; private Equipo[ ] equipos; } Indica que el partido NO se ha jugado Responsabilidades de la clase Campeonato • La clase Campeonato es la DUEÑA de los equipos y los goles, por lo tanto es la responsable de: – – – • Dar la información sobre los equipos Dar la información sobre la tabla de goles Dar la información sobre la tabla de posiciones Además es responsable de: – Registrar el resultado de un partido – Cargar la información del campeonato (de un archivo) y llenar con ella el arreglo de equipos Registrar el resultado de un partido El Campeonato de Fútbol – Mundo Registrar el Resultado de un Partido /** * Registra el resultado de un partido <br> * <b>pre: </b> Los equipos que participan en el campeonato ya * fueron inicializados. * <b>post: </b>Se actualizó la tabla de goles con el resultado indicado * * @param eq1 - Es el número del equipo 1 (Indice dentro de la matriz) * @param eq2 - Es el número del equipo 2 (Indice dentro de la matriz) * @param gol1 - Es el número de goles marcados por el equipo 1 * @param gol2 - Es el número de goles marcados por el equipo 2 * @throws Exception Se lanza excepción si: * los equipos no son válidos, * el número de goles es inválido * el partido ya se ha jugado antes */ Registrar el Resultado de un Partido public void registrarResultado(int eq1, int eq2, int gol1, int gol2) throws Exception { if ( eq1 < 0 || eq1 >= maxEquipos || eq2 < 0 || eq2 >= maxEquipos ) { throw new Exception("Equipos incorrectos"); } if ( eq1 == eq2 ) { throw new Exception(“Son el mismo equipo"); } if ( gol1 < 0 || gol2 < 0 ) n Verificación { throw new Exception(“Número de goles inválido"); los datos de } entrada if ( tablaGoles[ eq1 ][ eq2 ] != SIN_JUGAR || tablaGoles[ eq2 ][ eq1 ] != SIN_JUGAR ) { throw new Exception(“Partido ya jugado antes"); }… de Registrar el Resultado de un Partido public void registrarResultado(int eq1, int eq2, int gol1, int gol2) throws Exception { … tablaGoles[ eq1 ][ eq2 ] = gol1; tablaGoles[ eq2 ][ eq1 ] = gol2; o Asignación de los goles en las posiciones correctas de la matriz } gol1 eq1 gol2 eq2 Responsabilidades de la clase Campeonato • La clase Campeonato es la DUEÑA de los equipos y los goles, por lo tanto es la responsable de: – – – • Dar la información sobre los equipos Dar la información sobre la tabla de goles Dar la información sobre la tabla de posiciones Además es responsable de: – Registrar el resultado de un partido – Cargar la información del campeonato (de un archivo) y llenar con ella el arreglo de equipos Dar la información sobre la tabla de posiciones Construir la tabla de posiciones Construir la tabla de posiciones 0 A.C. Milan 0 Inter 1 2 Juventus 2 2 Roma 3 Lazio 4 SIN_JUGAR INVALIDO 1 2 1 0 3 4 tablaGoles[ 0 ] [ 1 ] < tablaGoles[ 1 ] [ 0 ] A.C. Milan ( índice 0 ) jugó contra el Inter ( índice 1 ) y perdió 1 a 2 Construir la tabla de posiciones 0 A.C. Milan 0 Inter 1 2 Juventus 2 2 Roma 3 Lazio 4 SIN_JUGAR INVALIDO 1 2 1 0 3 4 tablaGoles[ 0 ] [ 2 ] < tablaGoles[ 2 ] [ 0 ] A.C. Milan ( índice 0 ) jugó contra el Juventus ( índice 2 ) y perdió 0 a 2 Construir la tabla de posiciones • Para cada equipo, se debe calcular: 1. 2. 3. 4. 5. 6. 7. Partidos jugados (PJ) Partidos ganados (PG) Partidos empatados (PE) Partidos perdidos (PP) Goles a favor (GF) Goles en contra (GC) Puntos (Pts) SOLUCIÓN… Tarea No. 4 • Escriba un método de la clase Campeonato que calcule el número total de partidos ganados por el equipo que se recibe como parámetro. public int partidosGanados( int equipo ) { } Tarea No. 4 • Escriba un método de la clase Campeonato que calcule el número total de partidos empatados por el equipo que se recibe como parámetro. public int partidosEmpatados( int equipo ) { } Tarea No. 4 • Escriba un método de la clase Campeonato que calcule el número total de partidos jugados por el equipo que se recibe como parámetro. public int partidosJugados( int equipo ) { } Tarea No. 4 • Escriba un método de la clase Campeonato que calcule el número total de goles marcados por el equipo que se recibe como parámetro. public int golesAFavor( int equipo ) { } Tarea No. 4 • Escriba un método de la clase Campeonato que calcule el número total de puntos del equipo que se recibe como parámetro. Tenga en cuenta que un equipo recibe 3 puntos por cada partido ganado y un punto por cada partido empatado public int calcularTotalPuntos( int equipo ) { } Tarea No. 5 • Escriba un método de la clase Campeonato que retorne el índice del equipo que va ganando el campeonato. Si hay dos equipos con el mismo número de puntos, gana aquel cuya diferencia de goles (goles anotados menos goles recibidos) sea mayor. public int calcularGanador( ) { } Tarea No. 5 • Escriba un método de la clase Campeonato que calcule el número de partidos que faltan por jugar en el campeonato. public int calcularPorJugar( ) { } Tarea No. 5 • Escriba un método de la clase Campeonato que calcule el mayor número de goles marcados en un partido del campeonato (sumando los goles de los dos equipos). public int calcularTotalGoles( ) { } Tarea No. 5 • Escriba un método de la clase Campeonato que calcule el número de partidos del campeonato cuyo marcador fue cero a cero. public int calcularTotalCeroACero( ) { } Persistencia y manejo del estado inicial Responsabilidades de la clase Campeonato • La clase Campeonato es la DUEÑA de los equipos y los goles, por lo tanto es la responsable de: – – – • Dar la información sobre los equipos Dar la información sobre la tabla de goles Dar la información sobre la tabla de posiciones Además es responsable de: – Registrar el resultado de un partido – Cargar la información del campeonato (de un archivo) y llenar con ella el arreglo de equipos PERSISTENCIA … a 1/2 Persistencia es … • • • Guardar los datos resultado de un programa en un archivo para hacerlos persistentes cuando la aplicación termine. Es tema de APO2 !!! En APO1 … introducción a persistencia: – – Leer y cargar los datos de un archivo para configurar el estado inicial de los elementos del mundo Ejemplos: • • • Cargar las preguntas y respuestas del examen. Abrir una imagen. Ahora … leer la información de los equipos del campeonato para configurar el estado inicial del mundo. El Concepto de Archivo Qué es un archivo ? • • • Una entidad que contiene información y que es almacenada en la memoria secundaria del computador (disco duro, CD, unidad USB, diskette …) Tiene un nombre y una extensión El nombre tiene una ruta (camino o path en inglés) y el nombre corto propiamente dicho. c:/apoo/uniandes/cupi2/empleado/mundo/Empleado.java Qué es un archivo ? c:/apoo/uniandes/cupi2/empleado/mundo/Empleado.java • • • Ruta = estructura de directorios dentro de los cuales se encuentra el archivo, empezando por la raíz del disco “/”: separador de nombres de archivos Extensión: indica el tipo del archivo (doc para word, xls para excel, txt para texto, java para java, …) Ejemplo de archivos – Preguntas/Respuestas del Examen Ejemplo de archivos – Imagen BMP (bitmap) Es un archivo especial (no es texto plano). Solo se puede leer con una aplicación especializada (paint, photo shop, paint shop pro, visor de imágenes de APO1 !!!, …) Ejemplo de archivos – Equipos del campeonato Ejemplo de archivos – Equipos del campeonato En qué se parecen estos dos archivos ? Ejemplo de archivos – Equipos del campeonato R1// Número de elementos Ejemplo de archivos – Equipos del campeonato R2// Para cada elemento, el valor de sus propiedades Lectura de Archivos: desde la selección del archivo hasta la inicialización del estado del mundo de la aplicación * Selección del archivo nEl usuario genera un evento oprimiendo el botón. Se dispara el método cargarEquipos de la ventana principal (clase InterfazCampeonato) En el método cargarEquipos … public class InterfazCampeonato extends JFrame {… public void cargarEquipos( ) { JFileChooser fc = new JFileChooser( "./data" ); fc.setDialogTitle( "Abrir archivo de campeonato" ); int resultado = fc.showOpenDialog( this ); } } oSe muestra al usuario la caja de diálogo para seleccionar el archivo Caja de diálogo para escoger el archivo con la clase JFileChooser (JAVA) En el método cargarEquipos … public class InterfazCampeonato extends JFrame {… public void cargarEquipos( ) { File archivoCampeonato = null; JFileChooser fc = new JFileChooser( "./data" ); fc.setDialogTitle( "Abrir archivo de campeonato" ); int resultado = fc.showOpenDialog( this ); if( resultado == JFileChooser.APPROVE_OPTION ) { archivo = fc.getSelectedFile( ); } else { return; oSe muestra al usuario la } } } caja de diálogo para seleccionar el archivo Se obtiene el archivo seleccionado, que es un objeto de la clase File (de JAVA) En el método cargarEquipos … public class InterfazCampeonato extends JFrame {… public void cargarEquipos( ) { File archivoCampeonato = null; JFileChooser fc = new JFileChooser( "./data" ); fc.setDialogTitle( "Abrir archivo de campeonato" ); int resultado = fc.showOpenDialog( this ); if( resultado == JFileChooser.APPROVE_OPTION ) { archivoCampeonato = fc.getSelectedFile( ); campeonato = new Campeonato( archivoCampeonato ); } } } pSe crea el objeto campeonato Se llama al método constructor de la clase Campeonato, que recibe como parámetro, el archivo En el método constructor de la clase Campeonato … public class Campeonato {… public Campeonato( File arch ) { Properties datos = cargarInfoCampeonato( arch ); inicializarEquipos( datos ); inicializarTablaGoles( ); } } Se llama al método cargarInfoCampeonato de la clase Campeonato, que recibe como parámetro, el archivo qSe carga la información del campeonato, a partir del archivo En el método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File arch ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( arch ); datos.load( in ); in.close( ); return datos; } } Carga la información del archivo en un objeto de tipo Properties (JAVA) y retorna este objeto En el método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File arch ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( arch ); datos.load( in ); in.close( ); return datos; } } Crea el objeto de tipo Properties En el método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File arch ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( arch ); datos.load( in ); in.close( ); return datos; } } Crea un objeto de tipo FileInputStream (clase de JAVA que permite cargar archivos) ≅ Canal por donde se transmiten los datos desde el disco duro hasta el programa En el método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File arch ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( arch ); datos.load( in ); in.close( ); return datos; } } Carga el archivo mediante el método load En el método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File arch ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( arch ); datos.load( in ); in.close( ); return datos; } } Cierra el “canal” En el método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File arch ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( arch ); datos.load( in ); in.close( ); return datos; } } Retorna los “datos” que es un objeto de la clase Properties Qué contiene un objeto de la clase “Properties” ? • Clase Properties de JAVA – Representa un conjunto de propiedades “persistentes”, es decir que pueden ser almacenadas en un archivo. datos campeonato.equipos = 5 campeonato.nombre0 = A.C.Milan campeonato.nombre1 = Inter campeonato.nombre2 = Juventus campeonato.nombre3 = Roma campeonato.nombre4 = Lazio Archivo llamado “equipos.properties” (en el disco duro) campeonato.equipos 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Objeto llamado “datos” de tipo Properties (en memoria principal) El archivo equipos.properties • • Es un archivo de texto que contiene una lista de propiedades Cada propiedad es una línea del archivo y está definida como: – – nombre = valor campeonato.equipos = 5 Qué contiene un objeto de la clase “Properties” ? • Clase Properties de JAVA – Representa un conjunto de propiedades “persistentes”, es decir que pueden ser almacenadas en un archivo. datos campeonato.equipos = 5 campeonato.nombre0 = A.C.Milan campeonato.nombre1 = Inter campeonato.nombre2 = Juventus campeonato.nombre3 = Roma campeonato.nombre4 = Lazio Archivo llamado “equipos.properties” (en el disco duro) campeonato.equipos 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Objeto llamado “datos” de tipo Properties (en memoria principal) El objeto llamado “datos” de la clase Properties datos nombre = valor • • campeonato.equipos 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Tiene métodos para obtener el valor de sus elementos Ejemplo: – String valor = datos.getProperty(“campeonato.nombre0”) El objeto llamado “datos” de la clase Properties datos nombre = valor • • campeonato.equipos 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Retorna el “valor” del “nombre” campeonato.nombre0, que es “A.C. Milan” Tiene métodos para obtener el valor de sus elementos Ejemplo: – String valor = datos.getProperty(“campeonato.nombre0”) Volvamos al método cargarInfoCampeonato de la clase Campeonato … public class Campeonato {… private Properties cargarInfoCampeonato( File archivoInfoCampeonato ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( archivoInfoCampeonato ); datos.load( in ); in.close( ); datos return datos; campeonato.equipos 5 } } campeonato.nombre0 A.C.Milan Retorna los “datos” que es un objeto de la clase Properties campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Volvamos al método constructor de la clase Campeonato (paso 4) … public class Campeonato {… public Campeonato( File archivoInfoCampeonato ) { Properties datos = cargarInfoCampeonato( archivoInfoCampeonato ); inicializarEquipos( datos ); inicializarTablaGoles( maxEquipos ); } } datos campeonato.equipos 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Retorna los “datos” que es un objeto de la clase Properties qSe carga la información del campeonato, a partir del archivo Continuemos con el método constructor de la clase Campeonato (paso 5) … public class Campeonato {… public Campeonato( File archivoInfoCampeonato ) { Properties datos = cargarInfoCampeonato( archivoInfoCampeonato ); inicializarEquipos( datos ); inicializarTablaGoles( maxEquipos ); } } datos campeonato.equipos 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio rInicializa (llena) el arreglo de equipos a partir de la información contenida en el objeto “datos” En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); Obtiene el valor del nombre equipos = new Equipo[ numeroEquipos ]; “campeonato.equipos” que maxEquipos = numeroEquipos; contiene una cadena de caracteres con el número de equipos for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); equipos = new Equipo[ numeroEquipos ]; Obtiene el valor numérico de la maxEquipos = numeroEquipos; cantidad de equipos for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); equipos = new Equipo[ numeroEquipos ]; Crea el arreglo de equipos con maxEquipos = numeroEquipos; un tamaño igual a la cantidad de equipos for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); equipos = new Equipo[ numeroEquipos ]; Inicializa el atributo maxEquipos = numeroEquipos; for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); equipos = new Equipo[ numeroEquipos ]; Carga uno a uno los nombres maxEquipos = numeroEquipos; de los equipos for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); equipos = new Equipo[ numeroEquipos ]; Crea el objeto de la clase maxEquipos = numeroEquipos; Equipo for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio En el método inicializarEquipos de la clase Campeonato … public class Campeonato {… private void inicializarEquipos( Properties datos ) { String strNumeroEquipos = datos.getProperty("campeonato.equipos"); int numeroEquipos = Integer.parseInt(strNumeroEquipos); equipos = new Equipo[ numeroEquipos ]; maxEquipos = numeroEquipos; for (int i = 0; i < numeroEquipos; i++) { String nombreEquipo = datos.getProperty("campeonato.nombre"+ i); datos Equipo nuevo = new Equipo( nombreEquipo ); equipos[ i ] = nuevo; campeonato.equipos } } Adiciona el nuevo equipo al arreglo 5 campeonato.nombre0 A.C.Milan campeonato.nombre1 Inter campeonato.nombre2 Juventus campeonato.nombre3 Roma campeonato.nombre4 Lazio Continuemos con el método constructor de la clase Campeonato (paso 6) … public class Campeonato {… public Campeonato( File archivoInfoCampeonato ) { Properties datos = cargarInfoCampeonato( archivoInfoCampeonato ); inicializarEquipos( datos ); inicializarTablaGoles( maxEquipos ); } } 0 A.C. Milan 0 Inter 1 Juventus 2 Roma 3 Lazio 4 SIN_JUGAR 1 2 3 4 INVALIDO sInicializa la tabla de goles (con los valores SIN_JUGAR e INVALIDO) Volvamos al método cargarEquipos … public class InterfazCampeonato extends JFrame {… public void cargarEquipos( ) { File archivoCampeonato = null; JFileChooser fc = new JFileChooser( "./data" ); fc.setDialogTitle( "Abrir archivo de campeonato" ); int resultado = fc.showOpenDialog( this ); if( resultado == JFileChooser.APPROVE_OPTION ) { archivoCampeonato = fc.getSelectedFile( ); campeonato = new Campeonato( archivoCampeonato ); } } } Se llama al método constructor de la clase Campeonato, que recibe como parámetro, el archivo * Volvamos a la selección del archivo nEl usuario genera un evento oprimiendo el botón. Se dispara el método cargarEquipos de la ventana principal (clase InterfazCampeonato) En el método cargarInfoCampeonato de la clase Campeonato se pueden generar excepciones … (pag. 319) public class Campeonato {… private Properties cargarInfoCampeonato( File archivoInfoCampeonato ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( archivoInfoCampeonato ); datos.load( in ); in.close( ); return datos; Genera excepción si } no encuentra el } archivo En el método cargarInfoCampeonato de la clase Campeonato se pueden generar excepciones … (pag. 231) public class Campeonato {… private Properties cargarInfoCampeonato( File archivoInfoCampeonato ) { Properties datos = new Properties( ); FileInputStream in = new FileInputStream( archivoInfoCampeonato ); datos.load( in ); in.close( ); return datos; Genera excepción si } la manera como está } escrito el archivo no es “nombre=valor” Entonces … public class Campeonato {… private Properties cargarInfoCampeonato(File archivoInfoCampeonato) throws Exception { Properties datos = new Properties( ); FileInputStream in = null; try { Disparar la excepción si in = new FileInputStream(archivoInfoCampeonato); no encuentra el archivo } catch (Exception e) { throw new Exception("El archivo no existe!"); } try { datos.load(in); Disparar la excepción si in.close(); el archivo no es válido } catch (Exception e) { throw new Exception("El formato del archivo no es válido!"); } return datos; } }